From bce08ba2206668ad5c962903e29777bb7afa2de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 1 Dec 2011 15:17:10 +0100 Subject: [PATCH 01/74] Introducing QArrayData Modeled on QByteArrayData/QStringData/QVectorData, the intent is to unify book-keeping structs for array-like data and enable sharing of code among them. As in those structures, size (and alloc) data member(s) specify the number of *typed* elements the array does (and can) hold. The size or alignment requirements of those objects is not tracked in this data structure and needs to be maintained by its users. Contrary to QByteArrayData and QStringData, QArrayData's offset member keeps a *byte* offset to the actual data array and is computed from the beginning of the struct. Shared-null and -empty functionality is provided by QArrayData and shared among all users. Planned features include setSharable (force deep copies), fromRawData (detached header and data allocations) and literals a la QStringLiteral (static immutable instances), thus covering the functionality needed for QByteArray, QString and QVector. Change-Id: I9aa709dbb675442e6d06965efb8138ab84602bbd Reviewed-by: Bradley T. Hughes Reviewed-by: Olivier Goffart --- src/corelib/tools/qarraydata.cpp | 49 ++++++ src/corelib/tools/qarraydata.h | 97 +++++++++++ src/corelib/tools/tools.pri | 2 + .../corelib/tools/qarraydata/qarraydata.pro | 4 + .../tools/qarraydata/tst_qarraydata.cpp | 152 ++++++++++++++++++ tests/auto/corelib/tools/tools.pro | 1 + 6 files changed, 305 insertions(+) create mode 100644 src/corelib/tools/qarraydata.cpp create mode 100644 src/corelib/tools/qarraydata.h create mode 100644 tests/auto/corelib/tools/qarraydata/qarraydata.pro create mode 100644 tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp new file mode 100644 index 0000000000..3b15c0e246 --- /dev/null +++ b/src/corelib/tools/qarraydata.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +QT_BEGIN_NAMESPACE + +const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; +const QArrayData QArrayData::shared_empty = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h new file mode 100644 index 0000000000..0b6949fe48 --- /dev/null +++ b/src/corelib/tools/qarraydata.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATA_H +#define QARRAYDATA_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +struct Q_CORE_EXPORT QArrayData +{ + QtPrivate::RefCount ref; + int size; + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; // in bytes from beginning of header + + void *data() + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast(this) + offset; + } + + const void *data() const + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast(this) + offset; + } + + static const QArrayData shared_null; + static const QArrayData shared_empty; +}; + +template +struct QStaticArrayData +{ + QArrayData header; + T data[N]; +}; + +#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) { \ + Q_REFCOUNT_INITIALIZER(-1), size, 0, 0, \ + (sizeof(QArrayData) + (Q_ALIGNOF(type) - 1)) \ + & ~(Q_ALIGNOF(type) - 1) } \ + /**/ + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 13b597d513..199dd85354 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -2,6 +2,7 @@ HEADERS += \ tools/qalgorithms.h \ + tools/qarraydata.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ @@ -55,6 +56,7 @@ HEADERS += \ SOURCES += \ + tools/qarraydata.cpp \ tools/qbitarray.cpp \ tools/qbytearray.cpp \ tools/qbytearraymatcher.cpp \ diff --git a/tests/auto/corelib/tools/qarraydata/qarraydata.pro b/tests/auto/corelib/tools/qarraydata/qarraydata.pro new file mode 100644 index 0000000000..d384408d3b --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/qarraydata.pro @@ -0,0 +1,4 @@ +TARGET = tst_qarraydata +SOURCES += tst_qarraydata.cpp +QT = core testlib +CONFIG += testcase parallel_test diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp new file mode 100644 index 0000000000..31006c64ee --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +class tst_QArrayData : public QObject +{ + Q_OBJECT + +private slots: + void referenceCounting(); + void sharedNullEmpty(); + void staticData(); +}; + +void tst_QArrayData::referenceCounting() +{ + { + // Reference counting initialized to 1 (owned) + QArrayData array = { Q_REFCOUNT_INITIALIZER(1), 0, 0, 0, 0 }; + + QCOMPARE(int(array.ref), 1); + + array.ref.ref(); + QCOMPARE(int(array.ref), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(int(array.ref), 1); + + array.ref.ref(); + QCOMPARE(int(array.ref), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(int(array.ref), 1); + + QVERIFY(!array.ref.deref()); + QCOMPARE(int(array.ref), 0); + + // Now would be a good time to free/release allocated data + } + + { + // Reference counting initialized to -1 (static read-only data) + QArrayData array = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; + + QCOMPARE(int(array.ref), -1); + + array.ref.ref(); + QCOMPARE(int(array.ref), -1); + + QVERIFY(array.ref.deref()); + QCOMPARE(int(array.ref), -1); + } +} + +void tst_QArrayData::sharedNullEmpty() +{ + QArrayData *null = const_cast(&QArrayData::shared_null); + QArrayData *empty = const_cast(&QArrayData::shared_empty); + + QCOMPARE(int(null->ref), -1); + QCOMPARE(int(empty->ref), -1); + + null->ref.ref(); + empty->ref.ref(); + + QCOMPARE(int(null->ref), -1); + QCOMPARE(int(empty->ref), -1); + + QVERIFY(null->ref.deref()); + QVERIFY(empty->ref.deref()); + + QCOMPARE(int(null->ref), -1); + QCOMPARE(int(empty->ref), -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 charArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(char, 10), + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + }; + QStaticArrayData intArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QStaticArrayData 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(&charArray.data)); + QCOMPARE(intArray.header.data(), reinterpret_cast(&intArray.data)); + QCOMPARE(doubleArray.header.data(), reinterpret_cast(&doubleArray.data)); +} + +QTEST_APPLESS_MAIN(tst_QArrayData) +#include "tst_qarraydata.moc" diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index 930799e3b3..e2002a98b6 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -1,6 +1,7 @@ TEMPLATE=subdirs SUBDIRS=\ qalgorithms \ + qarraydata \ qbitarray \ qbytearray \ qbytearraymatcher \ From d5d073f87434fbe0f80269e9948b644f5ced1faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 2 Nov 2011 11:22:14 +0100 Subject: [PATCH 02/74] SimpleVector as a test case for QArrayData SimpleVector is meant solely as a test case and reference container implementation based on QArrayData functionality. It shall not replace QVector or friends. Change-Id: I5c66777c720f252c8e073a2884c6d5f1ac836d0e Reviewed-by: Olivier Goffart --- .../corelib/tools/qarraydata/qarraydata.pro | 1 + .../corelib/tools/qarraydata/simplevector.h | 183 ++++++++++++++++++ .../tools/qarraydata/tst_qarraydata.cpp | 105 ++++++++++ 3 files changed, 289 insertions(+) create mode 100644 tests/auto/corelib/tools/qarraydata/simplevector.h diff --git a/tests/auto/corelib/tools/qarraydata/qarraydata.pro b/tests/auto/corelib/tools/qarraydata/qarraydata.pro index d384408d3b..8e368117fa 100644 --- a/tests/auto/corelib/tools/qarraydata/qarraydata.pro +++ b/tests/auto/corelib/tools/qarraydata/qarraydata.pro @@ -1,4 +1,5 @@ TARGET = tst_qarraydata SOURCES += tst_qarraydata.cpp +HEADERS += simplevector.h QT = core testlib CONFIG += testcase parallel_test diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h new file mode 100644 index 0000000000..ad08ad5fdb --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QARRAY_TEST_SIMPLE_VECTOR_H +#define QARRAY_TEST_SIMPLE_VECTOR_H + +#include +#include + +template +struct SimpleVector +{ +private: + typedef QArrayData Data; + +public: + typedef T value_type; + typedef T *iterator; + typedef const T *const_iterator; + + SimpleVector() + : d(const_cast(&Data::shared_null)) + { + } + + SimpleVector(const SimpleVector &vec) + : d(vec.d) + { + d->ref.ref(); + } + + explicit SimpleVector(Data *ptr) + : d(ptr) + { + } + + ~SimpleVector() + { + if (!d->ref.deref()) + // Not implemented + Q_ASSERT(false); + } + + SimpleVector &operator=(const SimpleVector &vec) + { + SimpleVector temp(vec); + this->swap(temp); + return *this; + } + + bool empty() const { return d->size == 0; } + bool isNull() const { return d == &Data::shared_null; } + bool isEmpty() const { return this->empty(); } + + bool isSharedWith(const SimpleVector &other) const { return d == other.d; } + + size_t size() const { return d->size; } + size_t capacity() const { return d->alloc; } + + const_iterator begin() const { return static_cast(d->data()); } + const_iterator end() const { return static_cast(d->data()) + d->size; } + + const_iterator constBegin() const { return begin(); } + const_iterator constEnd() const { return end(); } + + const T &operator[](size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + const T &at(size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + + const T &front() const + { + Q_ASSERT(!isEmpty()); + return *begin(); + } + + const T &back() const + { + Q_ASSERT(!isEmpty()); + return *(end() - 1); + } + + void swap(SimpleVector &other) + { + qSwap(d, other.d); + } + + void clear() + { + SimpleVector tmp(d); + d = const_cast(&Data::shared_empty); + } + +private: + Data *d; +}; + +template +bool operator==(const SimpleVector &lhs, const SimpleVector &rhs) +{ + if (lhs.isSharedWith(rhs)) + return true; + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +bool operator!=(const SimpleVector &lhs, const SimpleVector &rhs) +{ + return !(lhs == rhs); +} + +template +bool operator<(const SimpleVector &lhs, const SimpleVector &rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + +template +bool operator>(const SimpleVector &lhs, const SimpleVector &rhs) +{ + return rhs < lhs; +} + +template +bool operator<=(const SimpleVector &lhs, const SimpleVector &rhs) +{ + return !(rhs < lhs); +} + +template +bool operator>=(const SimpleVector &lhs, const SimpleVector &rhs) +{ + return !(lhs < rhs); +} + +namespace std { + template + void swap(SimpleVector &v1, SimpleVector &v2) + { + v1.swap(v2); + } +} + +#endif // include guard diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 31006c64ee..1265876eb3 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -43,6 +43,8 @@ #include #include +#include "simplevector.h" + class tst_QArrayData : public QObject { Q_OBJECT @@ -51,6 +53,7 @@ private slots: void referenceCounting(); void sharedNullEmpty(); void staticData(); + void simpleVector(); }; void tst_QArrayData::referenceCounting() @@ -148,5 +151,107 @@ void tst_QArrayData::staticData() QCOMPARE(doubleArray.header.data(), reinterpret_cast(&doubleArray.data)); } +void tst_QArrayData::simpleVector() +{ + QArrayData data0 = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; + QStaticArrayData data1 = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7), + { 0, 1, 2, 3, 4, 5, 6 } + }; + + SimpleVector v1; + SimpleVector v2(v1); + SimpleVector v3(&data0); + SimpleVector v4(&data1.header); + SimpleVector v5(&data0); + SimpleVector v6(&data1.header); + + 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(v1.isEmpty()); + QVERIFY(v2.isEmpty()); + QVERIFY(v3.isEmpty()); + QVERIFY(v4.isEmpty()); + QVERIFY(v5.isEmpty()); + QVERIFY(!v6.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(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(v1.isSharedWith(v2)); + QVERIFY(v1.isSharedWith(v3)); + QVERIFY(!v1.isSharedWith(v4)); + QVERIFY(!v1.isSharedWith(v5)); + QVERIFY(!v1.isSharedWith(v6)); + + QVERIFY(v1.constBegin() == v1.constEnd()); + QVERIFY(v4.constBegin() == v4.constEnd()); + QVERIFY(v6.constBegin() + v6.size() == v6.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)); + + QCOMPARE(v6.front(), 0); + QCOMPARE(v6.back(), 6); + + for (size_t i = 0; i < v6.size(); ++i) { + QCOMPARE(v6[i], int(i)); + QCOMPARE(v6.at(i), int(i)); + QCOMPARE(&v6[i], &v6.at(i)); + } + + 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)); + } +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From 0806bc2d1b518b53681b3d8023def95ba8122630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 29 Nov 2011 18:12:05 +0100 Subject: [PATCH 03/74] Allocate/free support in QArrayData Centralizing QArrayData memory management decisions in one place will allow us to be smarter in how we allocate header and data. At the moment, these are allocated as a single block. In the future we may decide to allocate them separately for "large" data or specific alignment requirements. For users of QArrayData this remains transparent and not part of the ABI. The offset field in QArrayDataHeader enables this. This also hard-wires allocation of empty arrays to return shared_empty. Allocating detached headers (e.g., to support fromRawData) will thus require explicit support. Change-Id: Icac5a1f51ee7e468c76b4493d29debc18780e5dc Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydata.cpp | 44 ++++++ src/corelib/tools/qarraydata.h | 5 + .../tools/qarraydata/tst_qarraydata.cpp | 144 ++++++++++++++++++ 3 files changed, 193 insertions(+) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 3b15c0e246..168249c074 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -46,4 +46,48 @@ QT_BEGIN_NAMESPACE const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; const QArrayData QArrayData::shared_empty = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; +QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, + size_t capacity, bool reserve) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + + // Don't allocate empty headers + if (!capacity) + return const_cast(&shared_empty); + + // 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! + size_t allocSize = sizeof(QArrayData) + objectSize * capacity + + (alignment - Q_ALIGNOF(QArrayData)); + + QArrayData *header = static_cast(qMalloc(allocSize)); + Q_CHECK_PTR(header); + if (header) { + quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) + & ~(alignment - 1); + + header->ref = 1; + header->size = 0; + header->alloc = capacity; + header->capacityReserved = reserve; + header->offset = data - quintptr(header); + } + + return header; +} + +void QArrayData::deallocate(QArrayData *data, size_t objectSize, + size_t alignment) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + Q_UNUSED(objectSize) Q_UNUSED(alignment) + + qFree(data); +} + QT_END_NAMESPACE diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 0b6949fe48..684f00870d 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -73,6 +73,11 @@ struct Q_CORE_EXPORT QArrayData return reinterpret_cast(this) + offset; } + static QArrayData *allocate(size_t objectSize, size_t alignment, + size_t capacity, bool reserve) Q_REQUIRED_RESULT; + static void deallocate(QArrayData *data, size_t objectSize, + size_t alignment); + static const QArrayData shared_null; static const QArrayData shared_empty; }; diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 1265876eb3..7dfbd654a7 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -54,6 +54,10 @@ private slots: void sharedNullEmpty(); void staticData(); void simpleVector(); + void allocate_data(); + void allocate(); + void alignment_data(); + void alignment(); }; void tst_QArrayData::referenceCounting() @@ -253,5 +257,145 @@ void tst_QArrayData::simpleVector() } } +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 headers; +}; + +Q_DECLARE_METATYPE(const QArrayData *) + +void tst_QArrayData::allocate_data() +{ + QTest::addColumn("objectSize"); + QTest::addColumn("alignment"); + QTest::addColumn("isCapacityReserved"); + QTest::addColumn("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 *) } + }; + + struct { + char const *description; + bool isCapacityReserved; + const QArrayData *commonEmpty; + } options[] = { + { "Default", false, &QArrayData::shared_empty }, + { "Reserved", true, &QArrayData::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].isCapacityReserved << options[j].commonEmpty; +} + +void tst_QArrayData::allocate() +{ + QFETCH(size_t, objectSize); + QFETCH(size_t, alignment); + 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, + isCapacityReserved), 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); + keeper.headers.append(data); + + QCOMPARE(data->size, 0); + QVERIFY(data->alloc >= uint(capacity)); + QCOMPARE(data->capacityReserved, uint(isCapacityReserved)); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', objectSize * capacity); + } +} + +class Unaligned +{ + char dummy[8]; +}; + +void tst_QArrayData::alignment_data() +{ + QTest::addColumn("alignment"); + + for (int i = 1; i < 10; ++i) { + size_t alignment = 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, false); + 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(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); + } +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From 390eec325b25a142ce2204f1d1950621d4a519e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 29 Nov 2011 17:53:57 +0100 Subject: [PATCH 04/74] template struct QTypedArrayData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QTypedArrayData is a typed overlay for QArrayData, providing convenience and type-safety. It adds no data members to QArrayData, thus avoiding compiler-generated warnings for aliasing issues when casting back and forth. Change-Id: I969342a30989c4c14b3d03d0602e3d60a4cc0e9d Reviewed-by: João Abecasis --- src/corelib/tools/qarraydata.h | 42 ++++++ .../corelib/tools/qarraydata/simplevector.h | 34 +++-- .../tools/qarraydata/tst_qarraydata.cpp | 133 +++++++++++++++++- 3 files changed, 194 insertions(+), 15 deletions(-) diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 684f00870d..1312feccdb 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -82,6 +82,48 @@ struct Q_CORE_EXPORT QArrayData static const QArrayData shared_empty; }; +template +struct QTypedArrayData + : QArrayData +{ + typedef T *iterator; + typedef const T *const_iterator; + + T *data() { return static_cast(QArrayData::data()); } + const T *data() const { return static_cast(QArrayData::data()); } + + T *begin() { return data(); } + T *end() { return data() + size; } + const T *begin() const { return data(); } + const T *end() const { return data() + size; } + + class AlignmentDummy { QArrayData header; T data; }; + + static QTypedArrayData *allocate(size_t capacity, bool reserve = false) + Q_REQUIRED_RESULT + { + return static_cast(QArrayData::allocate(sizeof(T), + Q_ALIGNOF(AlignmentDummy), capacity, reserve)); + } + + static void deallocate(QArrayData *data) + { + QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy)); + } + + static QTypedArrayData *sharedNull() + { + return static_cast( + const_cast(&QArrayData::shared_null)); + } + + static QTypedArrayData *sharedEmpty() + { + return static_cast( + const_cast(&QArrayData::shared_empty)); + } +}; + template struct QStaticArrayData { diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index ad08ad5fdb..099045dfbb 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -50,15 +50,15 @@ template struct SimpleVector { private: - typedef QArrayData Data; + typedef QTypedArrayData Data; public: typedef T value_type; - typedef T *iterator; - typedef const T *const_iterator; + typedef typename Data::iterator iterator; + typedef typename Data::const_iterator const_iterator; SimpleVector() - : d(const_cast(&Data::shared_null)) + : d(Data::sharedNull()) { } @@ -68,6 +68,15 @@ public: d->ref.ref(); } + SimpleVector(size_t n, const T &t) + : d(Data::allocate(n)) + { + for (size_t i = 0; i < n; ++i) { + new (d->end()) T(t); + ++d->size; + } + } + explicit SimpleVector(Data *ptr) : d(ptr) { @@ -75,9 +84,12 @@ public: ~SimpleVector() { - if (!d->ref.deref()) - // Not implemented - Q_ASSERT(false); + if (!d->ref.deref()) { + const T *const data = d->data(); + while (d->size--) + data[d->size].~T(); + Data::deallocate(d); + } } SimpleVector &operator=(const SimpleVector &vec) @@ -88,7 +100,7 @@ public: } bool empty() const { return d->size == 0; } - bool isNull() const { return d == &Data::shared_null; } + bool isNull() const { return d == Data::sharedNull(); } bool isEmpty() const { return this->empty(); } bool isSharedWith(const SimpleVector &other) const { return d == other.d; } @@ -96,8 +108,8 @@ public: size_t size() const { return d->size; } size_t capacity() const { return d->alloc; } - const_iterator begin() const { return static_cast(d->data()); } - const_iterator end() const { return static_cast(d->data()) + d->size; } + const_iterator begin() const { return d->begin(); } + const_iterator end() const { return d->end(); } const_iterator constBegin() const { return begin(); } const_iterator constEnd() const { return end(); } @@ -125,7 +137,7 @@ public: void clear() { SimpleVector tmp(d); - d = const_cast(&Data::shared_empty); + d = Data::sharedEmpty(); } private: diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 7dfbd654a7..8bbef9bac9 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -58,6 +58,7 @@ private slots: void allocate(); void alignment_data(); void alignment(); + void typedData(); }; void tst_QArrayData::referenceCounting() @@ -165,10 +166,11 @@ void tst_QArrayData::simpleVector() SimpleVector v1; SimpleVector v2(v1); - SimpleVector v3(&data0); - SimpleVector v4(&data1.header); - SimpleVector v5(&data0); - SimpleVector v6(&data1.header); + SimpleVector v3(static_cast *>(&data0)); + SimpleVector v4(static_cast *>(&data1.header)); + SimpleVector v5(static_cast *>(&data0)); + SimpleVector v6(static_cast *>(&data1.header)); + SimpleVector v7(10, 5); v3 = v1; v1.swap(v3); @@ -180,6 +182,7 @@ void tst_QArrayData::simpleVector() QVERIFY(!v4.isNull()); QVERIFY(!v5.isNull()); QVERIFY(!v6.isNull()); + QVERIFY(!v7.isNull()); QVERIFY(v1.isEmpty()); QVERIFY(v2.isEmpty()); @@ -187,6 +190,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v4.isEmpty()); QVERIFY(v5.isEmpty()); QVERIFY(!v6.isEmpty()); + QVERIFY(!v7.isEmpty()); QCOMPARE(v1.size(), size_t(0)); QCOMPARE(v2.size(), size_t(0)); @@ -194,6 +198,7 @@ void tst_QArrayData::simpleVector() 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(v1.capacity(), size_t(0)); QCOMPARE(v2.capacity(), size_t(0)); @@ -201,6 +206,7 @@ void tst_QArrayData::simpleVector() 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(v1.isSharedWith(v2)); QVERIFY(v1.isSharedWith(v3)); @@ -211,6 +217,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v1.constBegin() == v1.constEnd()); QVERIFY(v4.constBegin() == v4.constEnd()); QVERIFY(v6.constBegin() + v6.size() == v6.constEnd()); + QVERIFY(v7.constBegin() + v7.size() == v7.constEnd()); QVERIFY(v1 == v2); QVERIFY(v1 == v3); @@ -241,6 +248,16 @@ void tst_QArrayData::simpleVector() QCOMPARE(&v6[i], &v6.at(i)); } + { + int count = 0; + Q_FOREACH (int value, v7) { + QCOMPARE(value, 5); + ++count; + } + + QCOMPARE(count, 10); + } + v5 = v6; QVERIFY(v5.isSharedWith(v6)); QVERIFY(!v1.isSharedWith(v5)); @@ -397,5 +414,113 @@ void tst_QArrayData::alignment() } } +void tst_QArrayData::typedData() +{ + QStaticArrayData 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 *array = + static_cast *>(&data.header); + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData::iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + const QTypedArrayData *array = + static_cast *>(&data.header); + + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData::const_iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + QTypedArrayData *null = QTypedArrayData::sharedNull(); + QTypedArrayData *empty = QTypedArrayData::sharedEmpty(); + + 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::AlignmentDummy)); + QArrayData *array = QTypedArrayData::allocate(10, false); + 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::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(short), + Q_ALIGNOF(QTypedArrayData::AlignmentDummy)); + QArrayData *array = QTypedArrayData::allocate(10, false); + 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::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(double), + Q_ALIGNOF(QTypedArrayData::AlignmentDummy)); + QArrayData *array = QTypedArrayData::allocate(10, false); + 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::deallocate(array); + + QVERIFY(true); + } +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From bd0b49efe0bf63b359fc315757a9de547d557802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 1 Dec 2011 12:54:50 +0100 Subject: [PATCH 05/74] Add test for GCC bug #43247 A bug has been reported against GCC 4.4.3 (present in other version as well), where the use of an array of size 1 to implement dynamic arrays (such as QVector) leads to incorrect results in optimized builds as the compiler assumes the index to be 0. This test tries to ensure QArrayDataHeader is not affected by this bug, as QVector currently is. Change-Id: Id701496bae4d74170de43399c1062da40eb078e7 Reviewed-by: Olivier Goffart --- .../tools/qarraydata/tst_qarraydata.cpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 8bbef9bac9..f7a2fa7979 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -59,6 +59,7 @@ private slots: void alignment_data(); void alignment(); void typedData(); + void gccBug43247(); }; void tst_QArrayData::referenceCounting() @@ -522,5 +523,32 @@ void tst_QArrayData::typedData() } } +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 array(10, 0); + // QVector 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); + } +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From 4da0b5fc02fe3a647d32892905c42928841d6487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 3 Nov 2011 12:52:26 +0100 Subject: [PATCH 06/74] QArrayDataOps: generic array operations This class, the selector and underlying implementations provide specialized operations on QArrayData, while allowing for optimized implementations that benefit from type-specific information. Currently, offering a generic implementation and specializations for PODs (trivial ctor, dtor and move operations) and movable types (can be trivially moved in memory). Change-Id: I2c5829b66c2aea79f12f21debe5c01f7104c7ea3 Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydataops.h | 182 ++++++++++++++++++ src/corelib/tools/tools.pri | 1 + .../corelib/tools/qarraydata/simplevector.h | 20 +- .../tools/qarraydata/tst_qarraydata.cpp | 127 ++++++++++++ 4 files changed, 323 insertions(+), 7 deletions(-) create mode 100644 src/corelib/tools/qarraydataops.h diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h new file mode 100644 index 0000000000..d62c411511 --- /dev/null +++ b/src/corelib/tools/qarraydataops.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAOPS_H +#define QARRAYDATAOPS_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +namespace QtPrivate { + +template +struct QPodArrayOps + : QTypedArrayData +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memcpy(this->end(), b, (e - b) * sizeof(T)); + this->size += e - b; + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) + ::memcpy(iter, &t, sizeof(T)); + this->size += n; + } + + void destroyAll() // Call from destructors, ONLY! + { + Q_ASSERT(this->ref == 0); + + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + } +}; + +template +struct QGenericArrayOps + : QTypedArrayData +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + T *iter = this->end(); + for (; b != e; ++iter, ++b) { + new (iter) T(*b); + ++this->size; + } + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) { + new (iter) T(t); + ++this->size; + } + } + + void destroyAll() // Call from destructors, ONLY + { + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + + Q_ASSERT(this->ref == 0); + + const T *const b = this->begin(); + const T *i = this->end(); + + while (i != b) + (--i)->~T(); + } +}; + +template +struct QMovableArrayOps + : QGenericArrayOps +{ + // using QGenericArrayOps::copyAppend; + // using QGenericArrayOps::destroyAll; +}; + +template +struct QArrayOpsSelector +{ + typedef QGenericArrayOps Type; +}; + +template +struct QArrayOpsSelector::isComplex && !QTypeInfo::isStatic + >::Type> +{ + typedef QPodArrayOps Type; +}; + +template +struct QArrayOpsSelector::isComplex && !QTypeInfo::isStatic + >::Type> +{ + typedef QMovableArrayOps Type; +}; + +} // namespace QtPrivate + +template +struct QArrayDataOps + : QtPrivate::QArrayOpsSelector::Type +{ +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 199dd85354..2ec32fd9e9 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -3,6 +3,7 @@ HEADERS += \ tools/qalgorithms.h \ tools/qarraydata.h \ + tools/qarraydataops.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 099045dfbb..36c9b7bc50 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -44,6 +44,8 @@ #define QARRAY_TEST_SIMPLE_VECTOR_H #include +#include + #include template @@ -51,6 +53,7 @@ struct SimpleVector { private: typedef QTypedArrayData Data; + typedef QArrayDataOps DataOps; public: typedef T value_type; @@ -71,10 +74,15 @@ public: SimpleVector(size_t n, const T &t) : d(Data::allocate(n)) { - for (size_t i = 0; i < n; ++i) { - new (d->end()) T(t); - ++d->size; - } + if (n) + static_cast(d)->copyAppend(n, t); + } + + SimpleVector(const T *begin, const T *end) + : d(Data::allocate(end - begin)) + { + if (end - begin) + static_cast(d)->copyAppend(begin, end); } explicit SimpleVector(Data *ptr) @@ -85,9 +93,7 @@ public: ~SimpleVector() { if (!d->ref.deref()) { - const T *const data = d->data(); - while (d->size--) - data[d->size].~T(); + static_cast(d)->destroyAll(); Data::deallocate(d); } } diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index f7a2fa7979..2972f0a678 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -41,6 +41,7 @@ #include +#include #include #include "simplevector.h" @@ -60,6 +61,7 @@ private slots: void alignment(); void typedData(); void gccBug43247(); + void arrayOps(); }; void tst_QArrayData::referenceCounting() @@ -165,6 +167,8 @@ void tst_QArrayData::simpleVector() { 0, 1, 2, 3, 4, 5, 6 } }; + int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + SimpleVector v1; SimpleVector v2(v1); SimpleVector v3(static_cast *>(&data0)); @@ -172,6 +176,7 @@ void tst_QArrayData::simpleVector() SimpleVector v5(static_cast *>(&data0)); SimpleVector v6(static_cast *>(&data1.header)); SimpleVector v7(10, 5); + SimpleVector v8(array, array + sizeof(array)/sizeof(*array)); v3 = v1; v1.swap(v3); @@ -184,6 +189,7 @@ void tst_QArrayData::simpleVector() QVERIFY(!v5.isNull()); QVERIFY(!v6.isNull()); QVERIFY(!v7.isNull()); + QVERIFY(!v8.isNull()); QVERIFY(v1.isEmpty()); QVERIFY(v2.isEmpty()); @@ -192,6 +198,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v5.isEmpty()); QVERIFY(!v6.isEmpty()); QVERIFY(!v7.isEmpty()); + QVERIFY(!v8.isEmpty()); QCOMPARE(v1.size(), size_t(0)); QCOMPARE(v2.size(), size_t(0)); @@ -200,6 +207,7 @@ void tst_QArrayData::simpleVector() 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)); @@ -208,6 +216,7 @@ void tst_QArrayData::simpleVector() 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.isSharedWith(v2)); QVERIFY(v1.isSharedWith(v3)); @@ -219,6 +228,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v4.constBegin() == v4.constEnd()); QVERIFY(v6.constBegin() + v6.size() == v6.constEnd()); QVERIFY(v7.constBegin() + v7.size() == v7.constEnd()); + QVERIFY(v8.constBegin() + v8.size() == v8.constEnd()); QVERIFY(v1 == v2); QVERIFY(v1 == v3); @@ -259,6 +269,16 @@ void tst_QArrayData::simpleVector() 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)); @@ -550,5 +570,112 @@ void tst_QArrayData::gccBug43247() } } +struct CountedObject +{ + CountedObject() + : id(liveCount++) + { + } + + CountedObject(const CountedObject &other) + : id(other.id) + { + ++liveCount; + } + + ~CountedObject() + { + --liveCount; + } + + CountedObject &operator=(const CountedObject &other) + { + id = other.id; + return *this; + } + + struct LeakChecker + { + LeakChecker() + : previousLiveCount(liveCount) + { + } + + ~LeakChecker() + { + QCOMPARE(liveCount, previousLiveCount); + } + + private: + const size_t previousLiveCount; + }; + + size_t id; // not unique + 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::isComplex && !QTypeInfo::isStatic); + QVERIFY(QTypeInfo::isComplex && !QTypeInfo::isStatic); + QVERIFY(QTypeInfo::isComplex && QTypeInfo::isStatic); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + for (size_t i = 0; i < 5; ++i) + QCOMPARE(objArray[i].id, i); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (I) + SimpleVector vi(intArray, intArray + 5); + SimpleVector vs(stringArray, stringArray + 5); + SimpleVector 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); + } + + //////////////////////////////////////////////////////////////////////////// + // 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(5, referenceInt); + vs = SimpleVector(5, referenceString); + vo = SimpleVector(5, referenceObject); + + 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); + } +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From 9c04f721a6b0521f596950771e9d88a5d00a29ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 22 Nov 2011 12:16:24 +0100 Subject: [PATCH 07/74] QArrayDataOps::insert Inserting elements anywhere in the array requires moving the elements that follow out of the way and writing in the new ones. Trivial for PODs and almost as much for movable types. For "complex" types, we start by extending the array with placement new and copy constructing elements. Then, copy assignment resets the elements that were previously part of the array. QPodArrayOps uses non-throwing operations. QMovableArrayOps provides full rollback in the face of exceptions (strong guarantee). QGenericArrayOps enforces that no data is leaked (all destructors called) and invariants are maintained on exceptions -- the basic guarantee. With 3 different implementations, 2 of which are non-trivial, this operation is a good showcase for QArrayOpsSelector and the different implementations. As such, it warrants its own commit. Change-Id: I21d9b4cb8e810db82623bcd1d78f583ebf3b6cb7 Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydataops.h | 142 ++++++++++++++++++ .../corelib/tools/qarraydata/simplevector.h | 107 +++++++++++++ .../tools/qarraydata/tst_qarraydata.cpp | 131 ++++++++++++++++ 3 files changed, 380 insertions(+) diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index d62c411511..e7c66a456b 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -88,6 +88,19 @@ struct QPodArrayOps // As this is to be called only from destructor, it doesn't need to be // exception safe; size not updated. } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + 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 + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memmove(where + (e - b), where, (this->end() - where) * sizeof(T)); + ::memcpy(where, b, (e - b) * sizeof(T)); + this->size += (e - b); + } }; template @@ -133,6 +146,71 @@ struct QGenericArrayOps while (i != b) (--i)->~T(); } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + 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 + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Array may be truncated at where in case of exceptions + + T *const end = this->end(); + const T *readIter = end; + T *writeIter = end + (e - b); + + const T *const step1End = where + qMax(e - b, end - where); + + struct Destructor + { + Destructor(T *&iter) + : iter(&iter) + , end(iter) + { + } + + void commit() + { + iter = &end; + } + + ~Destructor() + { + for (; *iter != end; --*iter) + (*iter)->~T(); + } + + T **iter; + T *end; + } destroyer(writeIter); + + // Construct new elements in array + do { + --readIter, --writeIter; + new (writeIter) T(*readIter); + } while (writeIter != step1End); + + while (writeIter != end) { + --e, --writeIter; + new (writeIter) T(*e); + } + + destroyer.commit(); + this->size += destroyer.end - end; + + // Copy assign over existing elements + while (readIter != where) { + --readIter, --writeIter; + *writeIter = *readIter; + } + + while (writeIter != where) { + --e, --writeIter; + *writeIter = *e; + } + } }; template @@ -141,6 +219,70 @@ struct QMovableArrayOps { // using QGenericArrayOps::copyAppend; // using QGenericArrayOps::destroyAll; + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + 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 + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Provides strong exception safety guarantee, + // provided T::~T() nothrow + + struct ReversibleDisplace + { + ReversibleDisplace(T *begin, T *end, size_t displace) + : begin(begin) + , end(end) + , displace(displace) + { + ::memmove(begin + displace, begin, (end - begin) * sizeof(T)); + } + + void commit() { displace = 0; } + + ~ReversibleDisplace() + { + if (displace) + ::memmove(begin, begin + displace, (end - begin) * sizeof(T)); + } + + T *const begin; + T *const end; + size_t displace; + + } displace(where, this->end(), size_t(e - b)); + + struct CopyConstructor + { + CopyConstructor(T *where) : where(where) {} + + void copy(const T *src, const T *const srcEnd) + { + n = 0; + for (; src != srcEnd; ++src) { + new (where + n) T(*src); + ++n; + } + n = 0; + } + + ~CopyConstructor() + { + while (n) + where[--n].~T(); + } + + T *const where; + size_t n; + } copier(where); + + copier.copy(b, e); + displace.commit(); + this->size += (e - b); + } }; template diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 36c9b7bc50..4aa3ab09c8 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -135,6 +135,113 @@ public: return *(end() - 1); } + void reserve(size_t n) + { + if (n > capacity() + || (n + && !d->capacityReserved + && (d->ref != 1 || (d->capacityReserved = 1, false)))) { + SimpleVector detached(Data::allocate(n, true)); + static_cast(detached.d)->copyAppend(constBegin(), constEnd()); + detached.swap(*this); + } + } + + void prepend(const_iterator first, const_iterator last) + { + if (!d->size) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + if (d->ref != 1 + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), d->capacityReserved)); + + static_cast(detached.d)->copyAppend(first, last); + static_cast(detached.d)->copyAppend(begin, begin + d->size); + detached.swap(*this); + + return; + } + + static_cast(d)->insert(begin, first, last); + } + + void append(const_iterator first, const_iterator last) + { + if (first == last) + return; + + if (d->ref != 1 + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), d->capacityReserved)); + + if (d->size) { + const T *const begin = constBegin(); + static_cast(detached.d)->copyAppend(begin, begin + d->size); + } + static_cast(detached.d)->copyAppend(first, last); + detached.swap(*this); + + return; + } + + static_cast(d)->copyAppend(first, last); + } + + void insert(int position, const_iterator first, const_iterator last) + { + if (position < 0) + position += d->size + 1; + + if (position <= 0) { + prepend(first, last); + return; + } + + if (size_t(position) >= size()) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + T *const where = begin + position; + const T *const end = begin + d->size; + if (d->ref != 1 + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), d->capacityReserved)); + + if (position) + static_cast(detached.d)->copyAppend(begin, where); + static_cast(detached.d)->copyAppend(first, last); + static_cast(detached.d)->copyAppend(where, end); + detached.swap(*this); + + return; + } + + // Temporarily copy overlapping data, if needed + if ((first >= where && first < end) + || (last > where && last <= end)) { + SimpleVector tmp(first, last); + static_cast(d)->insert(where, tmp.constBegin(), tmp.constEnd()); + return; + } + + static_cast(d)->insert(where, first, last); + } + void swap(SimpleVector &other) { qSwap(d, other.d); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 2972f0a678..808aa73c98 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -293,6 +293,67 @@ void tst_QArrayData::simpleVector() 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])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.append(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.insert(0, v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(30)); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.insert(10, v6.constBegin(), v6.constEnd()); + 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]); + + v1.insert(v1.size(), v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(90)); + + v1.insert(-1, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(100)); + + v1.insert(-11, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(110)); + + v1.insert(-200, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(120)); + + for (int i = 0; i < 120; ++i) + QCOMPARE(v1[i], v8[i % 10]); } struct Deallocator @@ -669,12 +730,82 @@ void tst_QArrayData::arrayOps() vs = SimpleVector(5, referenceString); vo = SimpleVector(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); } + + //////////////////////////////////////////////////////////////////////////// + // 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 < 15; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 15; i < 20; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } + + 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); + } + + for (int i = 25; i < 30; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } } QTEST_APPLESS_MAIN(tst_QArrayData) From 7d16ea40331dc4480af823288e6ed3ca58a677d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 2 Nov 2011 12:10:17 +0100 Subject: [PATCH 08/74] Introducing QArrayDataPointer This class provides RAII functionality for handling QArrayData pointers. Together with QArrayDataHeader and QArrayDataOps, this offers common boilerplate code for implementing a container which, itself, defines its own interface. Change-Id: If38eba22fbe8f69038a06fff4acb50af434d229e Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydatapointer.h | 161 ++++++++++++++++++ .../corelib/tools/qarraydata/simplevector.h | 60 ++----- 2 files changed, 179 insertions(+), 42 deletions(-) create mode 100644 src/corelib/tools/qarraydatapointer.h diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h new file mode 100644 index 0000000000..0d072a510e --- /dev/null +++ b/src/corelib/tools/qarraydatapointer.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAPOINTER_H +#define QARRAYDATAPOINTER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template +struct QArrayDataPointer +{ +private: + typedef QTypedArrayData Data; + typedef QArrayDataOps DataOps; + +public: + QArrayDataPointer() + : d(Data::sharedNull()) + { + } + + QArrayDataPointer(const QArrayDataPointer &other) + : d((other.d->ref.ref(), other.d)) + { + } + + explicit QArrayDataPointer(QTypedArrayData *ptr) + : d(ptr) + { + } + + QArrayDataPointer &operator=(const QArrayDataPointer &other) + { + QArrayDataPointer tmp(other); + this->swap(tmp); + return *this; + } + + DataOps &operator*() const + { + Q_ASSERT(d); + return *static_cast(d); + } + + DataOps *operator->() const + { + Q_ASSERT(d); + return static_cast(d); + } + + ~QArrayDataPointer() + { + if (!d->ref.deref()) { + (*this)->destroyAll(); + Data::deallocate(d); + } + } + + bool isNull() const + { + return d == Data::sharedNull(); + } + + Data *data() const + { + return d; + } + + void swap(QArrayDataPointer &other) + { + qSwap(d, other.d); + } + + void clear() + { + QArrayDataPointer tmp(d); + d = Data::sharedEmpty(); + } + +private: + Data *d; +}; + +template +inline bool operator==(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) +{ + return lhs.data() == rhs.data(); +} + +template +inline bool operator!=(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) +{ + return lhs.data() != rhs.data(); +} + +template +inline void qSwap(QArrayDataPointer &p1, QArrayDataPointer &p2) +{ + p1.swap(p2); +} + +QT_END_NAMESPACE + +namespace std +{ + template + inline void swap( + QT_PREPEND_NAMESPACE(QArrayDataPointer) &p1, + QT_PREPEND_NAMESPACE(QArrayDataPointer) &p2) + { + p1.swap(p2); + } +} + +QT_END_HEADER + +#endif // include guard diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 4aa3ab09c8..38f61189da 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -44,7 +44,7 @@ #define QARRAY_TEST_SIMPLE_VECTOR_H #include -#include +#include #include @@ -53,7 +53,6 @@ struct SimpleVector { private: typedef QTypedArrayData Data; - typedef QArrayDataOps DataOps; public: typedef T value_type; @@ -61,28 +60,21 @@ public: typedef typename Data::const_iterator const_iterator; SimpleVector() - : d(Data::sharedNull()) { } - SimpleVector(const SimpleVector &vec) - : d(vec.d) - { - d->ref.ref(); - } - SimpleVector(size_t n, const T &t) : d(Data::allocate(n)) { if (n) - static_cast(d)->copyAppend(n, t); + d->copyAppend(n, t); } SimpleVector(const T *begin, const T *end) : d(Data::allocate(end - begin)) { if (end - begin) - static_cast(d)->copyAppend(begin, end); + d->copyAppend(begin, end); } explicit SimpleVector(Data *ptr) @@ -90,23 +82,8 @@ public: { } - ~SimpleVector() - { - if (!d->ref.deref()) { - static_cast(d)->destroyAll(); - Data::deallocate(d); - } - } - - SimpleVector &operator=(const SimpleVector &vec) - { - SimpleVector temp(vec); - this->swap(temp); - return *this; - } - bool empty() const { return d->size == 0; } - bool isNull() const { return d == Data::sharedNull(); } + bool isNull() const { return d.isNull(); } bool isEmpty() const { return this->empty(); } bool isSharedWith(const SimpleVector &other) const { return d == other.d; } @@ -142,7 +119,7 @@ public: && !d->capacityReserved && (d->ref != 1 || (d->capacityReserved = 1, false)))) { SimpleVector detached(Data::allocate(n, true)); - static_cast(detached.d)->copyAppend(constBegin(), constEnd()); + detached.d->copyAppend(constBegin(), constEnd()); detached.swap(*this); } } @@ -163,14 +140,14 @@ public: SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), d->capacityReserved)); - static_cast(detached.d)->copyAppend(first, last); - static_cast(detached.d)->copyAppend(begin, begin + d->size); + detached.d->copyAppend(first, last); + detached.d->copyAppend(begin, begin + d->size); detached.swap(*this); return; } - static_cast(d)->insert(begin, first, last); + d->insert(begin, first, last); } void append(const_iterator first, const_iterator last) @@ -185,15 +162,15 @@ public: if (d->size) { const T *const begin = constBegin(); - static_cast(detached.d)->copyAppend(begin, begin + d->size); + detached.d->copyAppend(begin, begin + d->size); } - static_cast(detached.d)->copyAppend(first, last); + detached.d->copyAppend(first, last); detached.swap(*this); return; } - static_cast(d)->copyAppend(first, last); + d->copyAppend(first, last); } void insert(int position, const_iterator first, const_iterator last) @@ -223,9 +200,9 @@ public: qMax(capacity(), size() + (last - first)), d->capacityReserved)); if (position) - static_cast(detached.d)->copyAppend(begin, where); - static_cast(detached.d)->copyAppend(first, last); - static_cast(detached.d)->copyAppend(where, end); + detached.d->copyAppend(begin, where); + detached.d->copyAppend(first, last); + detached.d->copyAppend(where, end); detached.swap(*this); return; @@ -235,11 +212,11 @@ public: if ((first >= where && first < end) || (last > where && last <= end)) { SimpleVector tmp(first, last); - static_cast(d)->insert(where, tmp.constBegin(), tmp.constEnd()); + d->insert(where, tmp.constBegin(), tmp.constEnd()); return; } - static_cast(d)->insert(where, first, last); + d->insert(where, first, last); } void swap(SimpleVector &other) @@ -249,12 +226,11 @@ public: void clear() { - SimpleVector tmp(d); - d = Data::sharedEmpty(); + d.clear(); } private: - Data *d; + QArrayDataPointer d; }; template From 7fadc3ce322706ed6c36ad907e83fed1992b490e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 16 Dec 2011 17:22:45 +0100 Subject: [PATCH 09/74] Get rid of assignment operators in RefCount , and make it strictly a POD struct. Since this operator was only being used to set the initial (owned) value of the reference count, the name of the function introduced here to replace it makes that use case explicit. Change-Id: I2feadd2ac35dcb75ca211471baf5044a5f57cd62 Reviewed-by: Olivier Goffart Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.cpp | 2 +- src/corelib/tools/qbytearray.cpp | 16 ++++++++-------- src/corelib/tools/qhash.cpp | 2 +- src/corelib/tools/qlinkedlist.h | 2 +- src/corelib/tools/qlist.cpp | 4 ++-- src/corelib/tools/qmap.cpp | 2 +- src/corelib/tools/qrefcount.h | 6 ++---- src/corelib/tools/qstring.cpp | 16 ++++++++-------- src/corelib/tools/qvector.h | 8 ++++---- .../corelib/tools/qvector/qrawvector.h | 2 +- 10 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 168249c074..e3a6bad7c5 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -69,7 +69,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1); - header->ref = 1; + header->ref.initializeOwned(); header->size = 0; header->alloc = capacity; header->capacityReserved = reserve; diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index ed3f31fc43..f959effb43 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -576,7 +576,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) d.take(); // realloc was successful d.reset(p); } - d->ref = 1; + d->ref.initializeOwned(); d->size = len; d->alloc = len; d->capacityReserved = false; @@ -1304,7 +1304,7 @@ QByteArray::QByteArray(const char *str) int len = qstrlen(str); d = static_cast(qMalloc(sizeof(Data) + len + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = len; d->alloc = len; d->capacityReserved = false; @@ -1333,7 +1333,7 @@ QByteArray::QByteArray(const char *data, int size) } else { d = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; @@ -1357,7 +1357,7 @@ QByteArray::QByteArray(int size, char ch) } else { d = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; @@ -1377,7 +1377,7 @@ QByteArray::QByteArray(int size, Qt::Initialization) { d = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; @@ -1424,7 +1424,7 @@ void QByteArray::resize(int size) // Data *x = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = size; x->capacityReserved = false; @@ -1466,7 +1466,7 @@ void QByteArray::realloc(int alloc) if (d->ref != 1 || d->offset) { Data *x = static_cast(qMalloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = alloc; x->capacityReserved = d->capacityReserved; @@ -3887,7 +3887,7 @@ QByteArray QByteArray::fromRawData(const char *data, int size) } else { x = static_cast(qMalloc(sizeof(Data) + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index afb5ebe951..48a446cad6 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -196,7 +196,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), d = new QHashData; d->fakeNext = 0; d->buckets = 0; - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->nodeSize = nodeSize; d->userNumBits = userNumBits; diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 36cbc68eb8..55d229d351 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -253,7 +253,7 @@ void QLinkedList::detach_helper() { union { QLinkedListData *d; Node *e; } x; x.d = new QLinkedListData; - x.d->ref = 1; + x.d->ref.initializeOwned(); x.d->size = d->size; x.d->sharable = true; Node *original = e->n; diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 94be78ea21..8ef5fadb73 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -85,7 +85,7 @@ QListData::Data *QListData::detach_grow(int *idx, int num) Data* t = static_cast(qMalloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; + t->ref.initializeOwned(); t->sharable = true; t->alloc = alloc; // The space reservation algorithm's optimization is biased towards appending: @@ -127,7 +127,7 @@ QListData::Data *QListData::detach(int alloc) Data* t = static_cast(qMalloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; + t->ref.initializeOwned(); t->sharable = true; t->alloc = alloc; if (!alloc) { diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index f605a82dfe..6bac127a82 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -63,7 +63,7 @@ QMapData *QMapData::createData(int alignment) Node *e = reinterpret_cast(d); e->backward = e; e->forward[0] = e; - d->ref = 1; + d->ref.initializeOwned(); d->topLevel = 0; d->size = 0; d->randomBits = 0; diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 619f61e072..6d030cc0b3 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -75,10 +75,8 @@ public: { return !atomic.load(); } inline operator int() const { return atomic.load(); } - inline RefCount &operator=(int value) - { atomic.store(value); return *this; } - inline RefCount &operator=(const RefCount &other) - { atomic.store(other.atomic.load()); return *this; } + + void initializeOwned() { atomic.store(1); } QBasicAtomicInt atomic; }; diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index fb818e37b8..02d6788701 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1032,7 +1032,7 @@ QString::QString(const QChar *unicode, int size) } else { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1064,7 +1064,7 @@ QString::QString(const QChar *unicode) } else { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1089,7 +1089,7 @@ QString::QString(int size, QChar ch) } else { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1113,7 +1113,7 @@ QString::QString(int size, Qt::Initialization) { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1135,7 +1135,7 @@ QString::QString(QChar ch) { d = (Data *) qMalloc(sizeof(Data) + 2*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = 1; d->alloc = 1; d->capacityReserved = false; @@ -1314,7 +1314,7 @@ void QString::realloc(int alloc) if (d->ref != 1 || d->offset) { Data *x = static_cast(qMalloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = (uint) alloc; x->capacityReserved = d->capacityReserved; @@ -3758,7 +3758,7 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) size = qstrlen(str); d = static_cast(qMalloc(sizeof(Data) + (size+1) * sizeof(QChar))); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -7074,7 +7074,7 @@ QString QString::fromRawData(const QChar *unicode, int size) } else { x = static_cast(qMalloc(sizeof(Data) + sizeof(ushort))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index eab9311eb3..f408f6571f 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -411,7 +411,7 @@ template QVector::QVector(int asize) { d = malloc(asize); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = d->size = asize; d->sharable = true; d->capacity = false; @@ -429,7 +429,7 @@ template QVector::QVector(int asize, const T &t) { d = malloc(asize); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = d->size = asize; d->sharable = true; d->capacity = false; @@ -443,7 +443,7 @@ template QVector::QVector(std::initializer_list args) { d = malloc(int(args.size())); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = d->size = int(args.size()); d->sharable = true; d->capacity = false; @@ -515,7 +515,7 @@ void QVector::realloc(int asize, int aalloc) QT_RETHROW; } } - x.d->ref = 1; + x.d->ref.initializeOwned(); x.d->alloc = aalloc; x.d->sharable = true; x.d->capacity = d->capacity; diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index eb12a25403..f786d83a53 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -289,7 +289,7 @@ public: QVector mutateToVector() { Data *d = toBase(m_begin); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = m_alloc; d->size = m_size; d->sharable = 0; From 3fad9a846f6a89bd342b25c319d0263794bda575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 16 Dec 2011 16:21:29 +0100 Subject: [PATCH 10/74] Retire the generic Q_REFCOUNT_INITIALIZER macro This was only being used to initialize static read-only RefCount instances, where the value is hard-wired to -1. Instead of allowing initialization with arbitrary values (which for a reference count can be error prone) the intent of the macro is made explicit with its replacement Q_REFCOUNT_INITIALIZE_STATIC. Change-Id: I5b0f3f1eb58c3d010e49e9259ff4d06cbab2fd35 Reviewed-by: Olivier Goffart Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/tools/qarraydata.cpp | 4 ++-- src/corelib/tools/qarraydata.h | 2 +- src/corelib/tools/qbytearray.cpp | 4 ++-- src/corelib/tools/qbytearray.h | 4 ++-- src/corelib/tools/qhash.cpp | 2 +- src/corelib/tools/qlinkedlist.cpp | 2 +- src/corelib/tools/qlist.cpp | 2 +- src/corelib/tools/qmap.cpp | 2 +- src/corelib/tools/qrefcount.h | 4 ++-- src/corelib/tools/qstring.cpp | 4 ++-- src/corelib/tools/qstring.h | 4 ++-- src/corelib/tools/qvector.cpp | 2 +- tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 6 +++--- 13 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index e3a6bad7c5..1145ecb4f4 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -43,8 +43,8 @@ QT_BEGIN_NAMESPACE -const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; -const QArrayData QArrayData::shared_empty = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; +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 }; QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, size_t capacity, bool reserve) diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 1312feccdb..b7ab59257f 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -132,7 +132,7 @@ struct QStaticArrayData }; #define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) { \ - Q_REFCOUNT_INITIALIZER(-1), size, 0, 0, \ + Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, \ (sizeof(QArrayData) + (Q_ALIGNOF(type) - 1)) \ & ~(Q_ALIGNOF(type) - 1) } \ /**/ diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index f959effb43..09cc6d2f97 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -614,9 +614,9 @@ static inline char qToLower(char c) return c; } -const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), +const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }, { 0 } }; -const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), +const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }, { 0 } }; /*! diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 3ebeb3c340..932998cb4c 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -149,7 +149,7 @@ template struct QConstByteArrayDataPtr # define QByteArrayLiteral(str) ([]() -> QConstByteArrayDataPtr { \ enum { Size = sizeof(str) - 1 }; \ static const QConstByteArrayData qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ QConstByteArrayDataPtr holder = { &qbytearray_literal }; \ return holder; }()) @@ -162,7 +162,7 @@ template struct QConstByteArrayDataPtr __extension__ ({ \ enum { Size = sizeof(str) - 1 }; \ static const QConstByteArrayData qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ QConstByteArrayDataPtr holder = { &qbytearray_literal }; \ holder; }) #endif diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 48a446cad6..373b59872c 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -166,7 +166,7 @@ static int countBits(int hint) const int MinNumBits = 4; const QHashData QHashData::shared_null = { - 0, 0, Q_REFCOUNT_INITIALIZER(-1), 0, 0, MinNumBits, 0, 0, true, false, 0 + 0, 0, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, true, false, 0 }; void *QHashData::allocateNode(int nodeAlign) diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index 16105530bf..e5919363a0 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE const QLinkedListData QLinkedListData::shared_null = { const_cast(&QLinkedListData::shared_null), const_cast(&QLinkedListData::shared_null), - Q_REFCOUNT_INITIALIZER(-1), 0, true + Q_REFCOUNT_INITIALIZE_STATIC, 0, true }; /*! \class QLinkedList diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 8ef5fadb73..2a3695bb35 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE the number of elements in the list. */ -const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, true, { 0 } }; +const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, true, { 0 } }; static int grow(int size) { diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 6bac127a82..76cb203e10 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE const QMapData QMapData::shared_null = { const_cast(&shared_null), { const_cast(&shared_null), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, false, true, false, 0 + Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, false, true, false, 0 }; QMapData *QMapData::createData(int alignment) diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 6d030cc0b3..17be893744 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -81,10 +81,10 @@ public: QBasicAtomicInt atomic; }; -#define Q_REFCOUNT_INITIALIZER(a) { Q_BASIC_ATOMIC_INITIALIZER(a) } - } +#define Q_REFCOUNT_INITIALIZE_STATIC { Q_BASIC_ATOMIC_INITIALIZER(-1) } + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 02d6788701..16bf26721e 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -798,8 +798,8 @@ const QString::Null QString::null = { }; \sa split() */ -const QConstStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; -const QConstStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; +const QConstStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; +const QConstStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; int QString::grow(int size) { diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 4e1e67bfa6..ba3407e1cc 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -133,7 +133,7 @@ template struct QConstStringData # define QStringLiteral(str) ([]() -> QConstStringDataPtr { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ static const QConstStringData qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ QConstStringDataPtr holder = { &qstring_literal }; \ return holder; }()) @@ -146,7 +146,7 @@ template struct QConstStringData __extension__ ({ \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ static const QConstStringData qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ QConstStringDataPtr holder = { &qstring_literal }; \ holder; }) # endif diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index e1828c2b0f..95ae76eb1e 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -52,7 +52,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, true, false, 0 }; +const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, true, false, 0 }; QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) { diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 808aa73c98..241ef3b3e0 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -68,7 +68,7 @@ void tst_QArrayData::referenceCounting() { { // Reference counting initialized to 1 (owned) - QArrayData array = { Q_REFCOUNT_INITIALIZER(1), 0, 0, 0, 0 }; + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 }; QCOMPARE(int(array.ref), 1); @@ -92,7 +92,7 @@ void tst_QArrayData::referenceCounting() { // Reference counting initialized to -1 (static read-only data) - QArrayData array = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; + QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; QCOMPARE(int(array.ref), -1); @@ -161,7 +161,7 @@ void tst_QArrayData::staticData() void tst_QArrayData::simpleVector() { - QArrayData data0 = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 }; + QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; QStaticArrayData data1 = { Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7), { 0, 1, 2, 3, 4, 5, 6 } From 9a890a519e9e314b99ca765a7127aa8bdc5b8870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 25 Nov 2011 11:09:09 +0100 Subject: [PATCH 11/74] Add support for setSharable in RefCount A reference count of 0 (zero) would never change. RefCount::deref to zero would return false (resource should be freed), subsequent calls on the same state would return true and not change state. While safe from RefCount's side, calling deref on a reference count of zero potentially indicated a dangling reference. With this change, a reference count of 0 is now abused to imply a non-sharable instance (cf. QVector::setSharable). This instance is to be deleted upon deref(), as the data is not shared and has a single owner. In practice, this means an (intentional) change in behaviour in that deref'ing zero still won't change state, but will return false, turning previous access to dangling references into double free errors. Users of RefCount wanting to support non-sharable instances are required to check the return of RefCount::ref() and use RefCount::isShared() to determine whether to detach (instead of directly checking count == 1). New functions are introduced to determine whether RefCount indicates a "Static" (permanent, typically read-only) or "Sharable" instance and whether the instance is currently "Shared" and requires detaching prior to accepting modifications.. This change formalizes -1 as the value used to flag persistent, read-only instances, no longer reserving the full negative domain. The concrete value is part of the ABI, but not of the API. (isStatic and Q_REFCOUNT_INITIALIZE_STATIC are part of the API, instead) Change-Id: I9a63c844155319bef0411e02b47f9d92476afefe Reviewed-by: Thiago Macieira Reviewed-by: Bradley T. Hughes Reviewed-by: Robin Burchell --- src/corelib/tools/qrefcount.h | 40 +++++++++++- .../corelib/tools/qarraydata/simplevector.h | 2 + .../tools/qarraydata/tst_qarraydata.cpp | 63 +++++++++++++++++-- 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 17be893744..9478ff1269 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -56,17 +56,51 @@ namespace QtPrivate class RefCount { public: - inline void ref() { - if (atomic.load() > 0) + inline bool ref() { + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count != -1) // !isStatic atomic.ref(); + return true; } inline bool deref() { - if (atomic.load() <= 0) + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count == -1) // isStatic return true; return atomic.deref(); } + bool setSharable(bool sharable) + { + Q_ASSERT(!isShared()); + if (sharable) + return atomic.testAndSetRelaxed(0, 1); + else + return atomic.testAndSetRelaxed(1, 0); + } + + bool isStatic() const + { + // Persistent object, never deleted + return atomic.load() == -1; + } + + bool isSharable() const + { + // Sharable === Shared ownership. + return atomic.load() != 0; + } + + bool isShared() const + { + int count = atomic.load(); + return (count != 1) && (count != 0); + } + inline bool operator==(int value) const { return atomic.load() == value; } inline bool operator!=(int value) const diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 38f61189da..c5e19f3c55 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -86,6 +86,8 @@ public: bool isNull() const { return d.isNull(); } bool isEmpty() const { return this->empty(); } + bool isStatic() const { return d->ref.isStatic(); } + bool isShared() const { return d->ref.isShared(); } bool isSharedWith(const SimpleVector &other) const { return d == other.d; } size_t size() const { return d->size; } diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 241ef3b3e0..89a1f8bc75 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -72,13 +72,16 @@ void tst_QArrayData::referenceCounting() QCOMPARE(int(array.ref), 1); - array.ref.ref(); + QVERIFY(!array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); QCOMPARE(int(array.ref), 2); QVERIFY(array.ref.deref()); QCOMPARE(int(array.ref), 1); - array.ref.ref(); + QVERIFY(array.ref.ref()); QCOMPARE(int(array.ref), 2); QVERIFY(array.ref.deref()); @@ -90,13 +93,35 @@ void tst_QArrayData::referenceCounting() // Now would be a good time to free/release allocated data } + { + // Reference counting initialized to 0 (non-sharable) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + + QCOMPARE(int(array.ref), 0); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(!array.ref.isSharable()); + + QVERIFY(!array.ref.ref()); + // Reference counting fails, data should be copied + QCOMPARE(int(array.ref), 0); + + QVERIFY(!array.ref.deref()); + QCOMPARE(int(array.ref), 0); + + // Free/release data + } + { // Reference counting initialized to -1 (static read-only data) QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; QCOMPARE(int(array.ref), -1); - array.ref.ref(); + QVERIFY(array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); QCOMPARE(int(array.ref), -1); QVERIFY(array.ref.deref()); @@ -109,11 +134,19 @@ void tst_QArrayData::sharedNullEmpty() QArrayData *null = const_cast(&QArrayData::shared_null); QArrayData *empty = const_cast(&QArrayData::shared_empty); + QVERIFY(null->ref.isStatic()); + QVERIFY(null->ref.isSharable()); + QVERIFY(null->ref.isShared()); + + QVERIFY(empty->ref.isStatic()); + QVERIFY(empty->ref.isSharable()); + QVERIFY(empty->ref.isShared()); + QCOMPARE(int(null->ref), -1); QCOMPARE(int(empty->ref), -1); - null->ref.ref(); - empty->ref.ref(); + QVERIFY(null->ref.ref()); + QVERIFY(empty->ref.ref()); QCOMPARE(int(null->ref), -1); QCOMPARE(int(empty->ref), -1); @@ -218,6 +251,26 @@ void tst_QArrayData::simpleVector() 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(v7), v7.isShared())); + QVERIFY(!v7.isShared()); + QVERIFY(!v8.isShared()); + QVERIFY(v1.isSharedWith(v2)); QVERIFY(v1.isSharedWith(v3)); QVERIFY(!v1.isSharedWith(v4)); From fed603fde515339ec520accefded211ac6f69982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 4 Jan 2012 14:30:42 +0100 Subject: [PATCH 12/74] Ensure shared_null(s) are statically initialized on VS 2010 This removes const qualification on data members of QConst*Data, which was subjecting QString's and QByteArray's shared_null to the "order of static initialization fiasco", with up-to-date VS 2010. Furthermore, the const qualification in the places where it was removed had little meaning and no value. It was unnecessary. As such, "Const" was removed from the struct's names and "Static" used in its place, to imply their usefulness in supporting statically-initialized fixed-size (string and byte) containers. A test case was added to QArrayData as that is meant to replace both QStringData and QByteArrayData in the near future. VS issue reported at: https://connect.microsoft.com/VisualStudio/feedback/details/716461 Change-Id: I3d86f2a387a68f359bb3d8f4d10cf3da51c6ecf7 Reviewed-by: Thiago Macieira Reviewed-by: Olivier Goffart Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qbytearray.cpp | 4 +- src/corelib/tools/qbytearray.h | 28 ++++++------- src/corelib/tools/qstring.cpp | 4 +- src/corelib/tools/qstring.h | 42 +++++++++---------- src/corelib/tools/qstringbuilder.h | 8 ++-- .../tools/qarraydata/tst_qarraydata.cpp | 17 ++++++++ 6 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 09cc6d2f97..e56e4fc8ff 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -614,9 +614,9 @@ static inline char qToLower(char c) return c; } -const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, +const QStaticByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }, { 0 } }; -const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, +const QStaticByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }, { 0 } }; /*! diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 932998cb4c..dcaa6153f9 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -133,24 +133,24 @@ struct QByteArrayData inline const char *data() const { return d + sizeof(qptrdiff) + offset; } }; -template struct QConstByteArrayData +template struct QStaticByteArrayData { - const QByteArrayData ba; - const char data[N + 1]; + QByteArrayData ba; + char data[N + 1]; }; -template struct QConstByteArrayDataPtr +template struct QStaticByteArrayDataPtr { - const QConstByteArrayData *ptr; + const QStaticByteArrayData *ptr; }; #if defined(Q_COMPILER_LAMBDA) -# define QByteArrayLiteral(str) ([]() -> QConstByteArrayDataPtr { \ +# define QByteArrayLiteral(str) ([]() -> QStaticByteArrayDataPtr { \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData qbytearray_literal = \ + static const QStaticByteArrayData qbytearray_literal = \ { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr holder = { &qbytearray_literal }; \ + QStaticByteArrayDataPtr holder = { &qbytearray_literal }; \ return holder; }()) #elif defined(Q_CC_GNU) @@ -161,9 +161,9 @@ template struct QConstByteArrayDataPtr # define QByteArrayLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData qbytearray_literal = \ + static const QStaticByteArrayData qbytearray_literal = \ { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr holder = { &qbytearray_literal }; \ + QStaticByteArrayDataPtr holder = { &qbytearray_literal }; \ holder; }) #endif @@ -378,16 +378,16 @@ public: bool isNull() const; template - inline QByteArray(const QConstByteArrayData &dd) + inline QByteArray(const QStaticByteArrayData &dd) : d(const_cast(&dd.str)) {} template - Q_DECL_CONSTEXPR inline QByteArray(QConstByteArrayDataPtr dd) + Q_DECL_CONSTEXPR inline QByteArray(QStaticByteArrayDataPtr dd) : d(const_cast(&dd.ptr->ba)) {} private: operator QNoImplicitBoolCast() const; - static const QConstByteArrayData<1> shared_null; - static const QConstByteArrayData<1> shared_empty; + static const QStaticByteArrayData<1> shared_null; + static const QStaticByteArrayData<1> shared_empty; Data *d; QByteArray(Data *dd, int /*dummy*/, int /*dummy*/) : d(dd) {} void realloc(int alloc); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 16bf26721e..d57adbe731 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -798,8 +798,8 @@ const QString::Null QString::null = { }; \sa split() */ -const QConstStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; -const QConstStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; int QString::grow(int size) { diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index ba3407e1cc..898c80ce35 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -90,27 +90,27 @@ struct QStringData { inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } }; -template struct QConstStringData; -template struct QConstStringDataPtr +template struct QStaticStringData; +template struct QStaticStringDataPtr { - const QConstStringData *ptr; + const QStaticStringData *ptr; }; #if defined(Q_COMPILER_UNICODE_STRINGS) -template struct QConstStringData +template struct QStaticStringData { - const QStringData str; - const char16_t data[N + 1]; + QStringData str; + char16_t data[N + 1]; }; #define QT_UNICODE_LITERAL_II(str) u"" str #elif defined(Q_OS_WIN) || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) || defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536) // wchar_t is 2 bytes -template struct QConstStringData +template struct QStaticStringData { - const QStringData str; - const wchar_t data[N + 1]; + QStringData str; + wchar_t data[N + 1]; }; #if defined(Q_CC_MSVC) @@ -120,21 +120,21 @@ template struct QConstStringData #endif #else -template struct QConstStringData +template struct QStaticStringData { - const QStringData str; - const ushort data[N + 1]; + QStringData str; + ushort data[N + 1]; }; #endif #if defined(QT_UNICODE_LITERAL_II) # define QT_UNICODE_LITERAL(str) QT_UNICODE_LITERAL_II(str) # if defined(Q_COMPILER_LAMBDA) -# define QStringLiteral(str) ([]() -> QConstStringDataPtr { \ +# define QStringLiteral(str) ([]() -> QStaticStringDataPtr { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData qstring_literal = \ + static const QStaticStringData qstring_literal = \ { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr holder = { &qstring_literal }; \ + QStaticStringDataPtr holder = { &qstring_literal }; \ return holder; }()) # elif defined(Q_CC_GNU) @@ -145,9 +145,9 @@ template struct QConstStringData # define QStringLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData qstring_literal = \ + static const QStaticStringData qstring_literal = \ { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr holder = { &qstring_literal }; \ + QStaticStringDataPtr holder = { &qstring_literal }; \ holder; }) # endif #endif @@ -586,9 +586,9 @@ public: QString(int size, Qt::Initialization); template - inline QString(const QConstStringData &dd) : d(const_cast(&dd.str)) {} + inline QString(const QStaticStringData &dd) : d(const_cast(&dd.str)) {} template - Q_DECL_CONSTEXPR inline QString(QConstStringDataPtr dd) : d(const_cast(&dd.ptr->str)) {} + Q_DECL_CONSTEXPR inline QString(QStaticStringDataPtr dd) : d(const_cast(&dd.ptr->str)) {} private: #if defined(QT_NO_CAST_FROM_ASCII) && !defined(Q_NO_DECLARED_NOT_DEFINED) @@ -600,8 +600,8 @@ private: QString &operator=(const QByteArray &a); #endif - static const QConstStringData<1> shared_null; - static const QConstStringData<1> shared_empty; + static const QStaticStringData<1> shared_null; + static const QStaticStringData<1> shared_empty; Data *d; inline QString(Data *dd, int /*dummy*/) : d(dd) {} diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index 30b81c42f4..de6b655e95 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -249,9 +249,9 @@ template <> struct QConcatenable : private QAbstractConcatenable #endif }; -template struct QConcatenable > : private QAbstractConcatenable +template struct QConcatenable > : private QAbstractConcatenable { - typedef QConstStringDataPtr type; + typedef QStaticStringDataPtr type; typedef QString ConvertTo; enum { ExactSize = true }; static int size(const type &) { return N; } @@ -363,9 +363,9 @@ template <> struct QConcatenable : private QAbstractConcatenable } }; -template struct QConcatenable > : private QAbstractConcatenable +template struct QConcatenable > : private QAbstractConcatenable { - typedef QConstByteArrayDataPtr type; + typedef QStaticByteArrayDataPtr type; typedef QByteArray ConvertTo; enum { ExactSize = false }; static int size(const type &) { return N; } diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 89a1f8bc75..63a26ed926 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -46,6 +46,23 @@ #include "simplevector.h" +struct SharedNullVerifier +{ + SharedNullVerifier() + { + Q_ASSERT(QArrayData::shared_null.ref.isStatic()); + Q_ASSERT(QArrayData::shared_null.ref.isShared()); + Q_ASSERT(QArrayData::shared_null.ref.isSharable()); + } +}; + +// 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 From b29338e80588b97efdb57d62cd3ca474f16db965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 25 Nov 2011 13:16:24 +0100 Subject: [PATCH 13/74] Add test for QVector::setSharable Change-Id: Id31761bfb642d4ce515768c1ffe1e3088d883353 Reviewed-by: Bradley T. Hughes Reviewed-by: Thiago Macieira --- .../corelib/tools/qvector/tst_qvector.cpp | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index acf3756258..704d75b709 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -85,6 +85,8 @@ private slots: void initializeList(); void const_shared_null(); + void setSharable_data(); + void setSharable(); }; void tst_QVector::constructors() const @@ -946,5 +948,97 @@ void tst_QVector::const_shared_null() QVERIFY(!v2.isDetached()); } +Q_DECLARE_METATYPE(QVector); + +void tst_QVector::setSharable_data() +{ + QTest::addColumn >("vector"); + QTest::addColumn("size"); + QTest::addColumn("capacity"); + QTest::addColumn("isCapacityReserved"); + + QVector null; + QVector empty(0, 5); + QVector emptyReserved; + QVector nonEmpty; + QVector nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + + nonEmpty << 0 << 1 << 2 << 3 << 4; + nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; + + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 0 << 0 << false; + QTest::newRow("empty") << empty << 0 << 0 << false; + QTest::newRow("empty, Reserved") << emptyReserved << 0 << 10 << true; + QTest::newRow("non-empty") << nonEmpty << 5 << 0 << false; + QTest::newRow("non-empty, Reserved") << nonEmptyReserved << 7 << 15 << true; +} + +void tst_QVector::setSharable() +{ + QFETCH(QVector, vector); + QFETCH(int, size); + QFETCH(int, capacity); + QFETCH(bool, isCapacityReserved); + + QVERIFY(!vector.isDetached()); // Shared with QTest + + vector.setSharable(true); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + + { + QVector copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + vector.setSharable(false); + QVERIFY(vector.isDetached() || vector.isSharedWith(QVector())); + + { + QVector copy(vector); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QVector())); + QCOMPARE(copy.size(), size); + if (isCapacityReserved) + QVERIFY2(copy.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + QCOMPARE(copy, vector); + } + + vector.setSharable(true); + + { + QVector copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + for (int i = 0; i < vector.size(); ++i) + QCOMPARE(vector[i], i); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); +} + QTEST_APPLESS_MAIN(tst_QVector) #include "tst_qvector.moc" From 51048e1f31df5be45e71a75fc535111dd36c4c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 24 Nov 2011 17:22:37 +0100 Subject: [PATCH 14/74] Adding detach to QArrayDataPointer Detaching operations added to SimpleVector Change-Id: I5f549582cf579569f08cb8d53a6d12fe32b862e6 Reviewed-by: Bradley T. Hughes Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydatapointer.h | 24 ++++++++++++ .../corelib/tools/qarraydata/simplevector.h | 25 +++++++++++++ .../tools/qarraydata/tst_qarraydata.cpp | 37 ++++++++++++++++--- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 0d072a510e..f1cd1dc4b1 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -121,7 +121,31 @@ public: d = Data::sharedEmpty(); } + bool detach() + { + if (d->ref.isShared()) { + Data *copy = clone(); + QArrayDataPointer old(d); + d = copy; + return true; + } + + return false; + } + private: + Data *clone() const Q_REQUIRED_RESULT + { + QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size, + d->capacityReserved)); + if (d->size) + copy->copyAppend(d->begin(), d->end()); + + Data *result = copy.d; + copy.d = Data::sharedNull(); + return result; + } + Data *d; }; diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index c5e19f3c55..9ab28a9ddd 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -93,15 +93,35 @@ public: size_t size() const { return d->size; } size_t capacity() const { return d->alloc; } + iterator begin() { detach(); return d->begin(); } + iterator end() { detach(); return d->end(); } + const_iterator begin() const { return d->begin(); } const_iterator end() const { return d->end(); } const_iterator constBegin() const { return begin(); } const_iterator constEnd() const { return end(); } + T &operator[](size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + T &at(size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + const T &operator[](size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } const T &at(size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + T &front() + { + Q_ASSERT(!isEmpty()); + detach(); + return *begin(); + } + + T &back() + { + Q_ASSERT(!isEmpty()); + detach(); + return *(end() - 1); + } + const T &front() const { Q_ASSERT(!isEmpty()); @@ -231,6 +251,11 @@ public: d.clear(); } + void detach() + { + d.detach(); + } + private: QArrayDataPointer d; }; diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 63a26ed926..e9a3218331 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -81,6 +81,8 @@ private slots: void arrayOps(); }; +template const T &const_(const T &t) { return t; } + void tst_QArrayData::referenceCounting() { { @@ -320,13 +322,36 @@ void tst_QArrayData::simpleVector() QVERIFY(v6 >= v1); QVERIFY(!(v1 >= v6)); - QCOMPARE(v6.front(), 0); - QCOMPARE(v6.back(), 6); + { + SimpleVector temp(v6); - for (size_t i = 0; i < v6.size(); ++i) { - QCOMPARE(v6[i], int(i)); - QCOMPARE(v6.at(i), int(i)); - QCOMPARE(&v6[i], &v6.at(i)); + 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(temp.begin(), tempBegin); } { From 3d61c5ca8f00b489435d5bea776b4a4c97c39793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 24 Nov 2011 17:15:11 +0100 Subject: [PATCH 15/74] Add setSharable support in QArrayData stack Making use of the same feature added in RefCount. To keep with the intention of avoiding the allocation of "empty" array headers, this introduces an unsharable_empty, which allows users to maintain the "unsharable bit" on empty containers, without imposing any actual allocations. (Before anyone asks, there is no point to a zero-sized capacity-reserved container so no other combinations are needed for now.) Change-Id: Icaa40ac3100ad954fdc20dee0c991861136a5b19 Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydata.cpp | 12 +- src/corelib/tools/qarraydata.h | 15 +- src/corelib/tools/qarraydataops.h | 14 +- src/corelib/tools/qarraydatapointer.h | 20 ++- src/corelib/tools/qrefcount.h | 1 + .../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(&shared_empty); + return sharable + ? const_cast(&shared_empty) + : const_cast(&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 @@ -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(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( const_cast(&QArrayData::shared_empty)); } + + static QTypedArrayData *unsharableEmpty() + { + return static_cast( + const_cast(&QArrayData::unsharable_empty)); + } }; template 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 const T &const_(const T &t) { return t; } @@ -477,6 +479,7 @@ void tst_QArrayData::allocate_data() QTest::addColumn("objectSize"); QTest::addColumn("alignment"); QTest::addColumn("isCapacityReserved"); + QTest::addColumn("isSharable"); QTest::addColumn("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) + +static inline bool arrayIsFilledWith(const QArrayDataPointer &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 >("array"); + QTest::addColumn("size"); + QTest::addColumn("capacity"); + QTest::addColumn("isCapacityReserved"); + QTest::addColumn("fillValue"); + + QArrayDataPointer null; + QArrayDataPointer empty; empty.clear(); + + static QStaticArrayData staticArrayData = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } + }; + + QArrayDataPointer emptyReserved(QTypedArrayData::allocate(5, true, true)); + QArrayDataPointer nonEmpty(QTypedArrayData::allocate(10, false, true)); + QArrayDataPointer nonEmptyReserved(QTypedArrayData::allocate(15, true, true)); + QArrayDataPointer staticArray(static_cast *>(&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, 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 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 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 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" From d91b4f0b13926a0861d7e172f14437d20d06332e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 5 Jan 2012 16:29:42 +0100 Subject: [PATCH 16/74] Remove shared_empty and unsharable_empty from API They still exist and help avoid allocation of "empty" array headers, but they're no longer part of the public API, thus reducing relocatable symbols and relocations in inline code. This means an extra non-inline call on QArrayDataPointer::clear and setSharable operations, which are (expensive) detaching operations, anyway. Change-Id: Iea804e5ddc8af55ebc0951ca17a7a4e8401abc55 Reviewed-by: Bradley T. Hughes Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.cpp | 11 ++++++----- src/corelib/tools/qarraydata.h | 14 -------------- src/corelib/tools/qarraydatapointer.h | 9 ++------- .../tools/qarraydata/tst_qarraydata.cpp | 18 ++++++++++++------ 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index c6e96c78a9..150f23cc12 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -44,8 +44,9 @@ 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 }; + +static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; +static const QArrayData qt_array_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, bool sharable) @@ -57,8 +58,8 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, // Don't allocate empty headers if (!capacity) return sharable - ? const_cast(&shared_empty) - : const_cast(&unsharable_empty); + ? const_cast(&qt_array_empty) + : const_cast(&qt_array_unsharable_empty); // Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we // can properly align the data array. This assumes malloc is able to @@ -90,7 +91,7 @@ void QArrayData::deallocate(QArrayData *data, size_t objectSize, && !(alignment & (alignment - 1))); Q_UNUSED(objectSize) Q_UNUSED(alignment) - if (data == &unsharable_empty) + if (data == &qt_array_unsharable_empty) return; qFree(data); diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 1fd60e2155..2486bebafa 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -79,8 +79,6 @@ struct Q_CORE_EXPORT QArrayData size_t alignment); static const QArrayData shared_null; - static const QArrayData shared_empty; - static const QArrayData unsharable_empty; }; template @@ -117,18 +115,6 @@ struct QTypedArrayData return static_cast( const_cast(&QArrayData::shared_null)); } - - static QTypedArrayData *sharedEmpty() - { - return static_cast( - const_cast(&QArrayData::shared_empty)); - } - - static QTypedArrayData *unsharableEmpty() - { - return static_cast( - const_cast(&QArrayData::unsharable_empty)); - } }; template diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index e42d146c58..c03e2ef849 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -115,12 +115,7 @@ public: 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(); + d = Data::allocate(0, false, sharable); return; } @@ -136,7 +131,7 @@ public: void clear() { QArrayDataPointer tmp(d); - d = Data::sharedEmpty(); + d = Data::allocate(0); } bool detach() diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index e8edab20e4..47d5e2a32b 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -153,7 +153,7 @@ void tst_QArrayData::referenceCounting() void tst_QArrayData::sharedNullEmpty() { QArrayData *null = const_cast(&QArrayData::shared_null); - QArrayData *empty = const_cast(&QArrayData::shared_empty); + QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0, false, true); QVERIFY(null->ref.isStatic()); QVERIFY(null->ref.isSharable()); @@ -492,16 +492,22 @@ void tst_QArrayData::allocate_data() { "void *", sizeof(void *), Q_ALIGNOF(void *) } }; + QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, true); + QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, false); + + QVERIFY(shared_empty); + QVERIFY(unsharable_empty); + struct { char const *description; bool isCapacityReserved; bool isSharable; const QArrayData *commonEmpty; } options[] = { - { "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 }, + { "Default", false, true, shared_empty }, + { "Reserved", true, true, shared_empty }, + { "Reserved | Unsharable", true, false, unsharable_empty }, + { "Unsharable", false, false, unsharable_empty }, }; for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) @@ -635,7 +641,7 @@ void tst_QArrayData::typedData() { QTypedArrayData *null = QTypedArrayData::sharedNull(); - QTypedArrayData *empty = QTypedArrayData::sharedEmpty(); + QTypedArrayData *empty = QTypedArrayData::allocate(0); QVERIFY(null != empty); From f1e48d48fd58b28b1dc18652af3fc74da5e112fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 22 Nov 2011 17:28:14 +0100 Subject: [PATCH 17/74] Add AllocateOptions to QArrayData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This approach is better for future ABI evolution than using individual bool parameters. QArrayData now also offers to calculate allocate options for typical detach and clone operations: the CapacityReserved flag is preserved, while cloning resets the Unsharable state. Change-Id: I256e135adcf27a52a5c7d6130069c35c8b946bc3 Reviewed-by: João Abecasis --- src/corelib/tools/qarraydata.cpp | 8 ++-- src/corelib/tools/qarraydata.h | 37 ++++++++++++++-- src/corelib/tools/qarraydatapointer.h | 12 +++--- .../corelib/tools/qarraydata/simplevector.h | 12 ++++-- .../tools/qarraydata/tst_qarraydata.cpp | 42 ++++++++++++------- 5 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 150f23cc12..efed984aef 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -49,7 +49,7 @@ static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0 static const QArrayData qt_array_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, bool sharable) + size_t capacity, AllocateOptions options) { // Alignment is a power of two Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) @@ -57,7 +57,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, // Don't allocate empty headers if (!capacity) - return sharable + return !(options & Unsharable) ? const_cast(&qt_array_empty) : const_cast(&qt_array_unsharable_empty); @@ -73,10 +73,10 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1); - header->ref.atomic.store(sharable ? 1 : 0); + header->ref.atomic.store(bool(!(options & Unsharable))); header->size = 0; header->alloc = capacity; - header->capacityReserved = reserve; + header->capacityReserved = bool(options & CapacityReserved); header->offset = data - quintptr(header); } diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 2486bebafa..8eb543ee51 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -73,14 +73,43 @@ struct Q_CORE_EXPORT QArrayData return reinterpret_cast(this) + offset; } + enum AllocateOption { + CapacityReserved = 0x1, + Unsharable = 0x2, + + Default = 0 + }; + + Q_DECLARE_FLAGS(AllocateOptions, AllocateOption) + + AllocateOptions detachFlags() const + { + AllocateOptions result; + if (!ref.isSharable()) + result |= Unsharable; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + AllocateOptions cloneFlags() const + { + AllocateOptions result; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + static QArrayData *allocate(size_t objectSize, size_t alignment, - size_t capacity, bool reserve, bool sharable) Q_REQUIRED_RESULT; + size_t capacity, AllocateOptions options = Default) Q_REQUIRED_RESULT; static void deallocate(QArrayData *data, size_t objectSize, size_t alignment); static const QArrayData shared_null; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocateOptions) + template struct QTypedArrayData : QArrayData @@ -98,11 +127,11 @@ struct QTypedArrayData class AlignmentDummy { QArrayData header; T data; }; - static QTypedArrayData *allocate(size_t capacity, bool reserve = false, - bool sharable = true) Q_REQUIRED_RESULT + static QTypedArrayData *allocate(size_t capacity, + AllocateOptions options = Default) Q_REQUIRED_RESULT { return static_cast(QArrayData::allocate(sizeof(T), - Q_ALIGNOF(AlignmentDummy), capacity, reserve, sharable)); + Q_ALIGNOF(AlignmentDummy), capacity, options)); } static void deallocate(QArrayData *data) diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index c03e2ef849..1dc02daa63 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -66,7 +66,7 @@ public: QArrayDataPointer(const QArrayDataPointer &other) : d(other.d->ref.ref() ? other.d - : other.clone()) + : other.clone(other.d->cloneFlags())) { } @@ -115,7 +115,9 @@ public: void setSharable(bool sharable) { if (d->alloc == 0 && d->size == 0) { - d = Data::allocate(0, false, sharable); + d = Data::allocate(0, sharable + ? QArrayData::Default + : QArrayData::Unsharable); return; } @@ -137,7 +139,7 @@ public: bool detach() { if (d->ref.isShared()) { - Data *copy = clone(); + Data *copy = clone(d->detachFlags()); QArrayDataPointer old(d); d = copy; return true; @@ -147,10 +149,10 @@ public: } private: - Data *clone() const Q_REQUIRED_RESULT + Data *clone(QArrayData::AllocateOptions options) const Q_REQUIRED_RESULT { QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size, - d->capacityReserved)); + options)); if (d->size) copy->copyAppend(d->begin(), d->end()); diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 9ab28a9ddd..4f02df1c40 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -140,7 +140,8 @@ public: || (n && !d->capacityReserved && (d->ref != 1 || (d->capacityReserved = 1, false)))) { - SimpleVector detached(Data::allocate(n, true)); + SimpleVector detached(Data::allocate(n, + d->detachFlags() | Data::CapacityReserved)); detached.d->copyAppend(constBegin(), constEnd()); detached.swap(*this); } @@ -160,7 +161,8 @@ public: if (d->ref != 1 || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( - qMax(capacity(), size() + (last - first)), d->capacityReserved)); + qMax(capacity(), size() + (last - first)), + d->detachFlags())); detached.d->copyAppend(first, last); detached.d->copyAppend(begin, begin + d->size); @@ -180,7 +182,8 @@ public: if (d->ref != 1 || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( - qMax(capacity(), size() + (last - first)), d->capacityReserved)); + qMax(capacity(), size() + (last - first)), + d->detachFlags())); if (d->size) { const T *const begin = constBegin(); @@ -219,7 +222,8 @@ public: if (d->ref != 1 || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( - qMax(capacity(), size() + (last - first)), d->capacityReserved)); + qMax(capacity(), size() + (last - first)), + d->detachFlags())); if (position) detached.d->copyAppend(begin, where); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 47d5e2a32b..2df4131f4a 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -153,7 +153,7 @@ void tst_QArrayData::referenceCounting() void tst_QArrayData::sharedNullEmpty() { QArrayData *null = const_cast(&QArrayData::shared_null); - QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0, false, true); + QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0); QVERIFY(null->ref.isStatic()); QVERIFY(null->ref.isSharable()); @@ -473,11 +473,13 @@ struct Deallocator }; Q_DECLARE_METATYPE(const QArrayData *) +Q_DECLARE_METATYPE(QArrayData::AllocateOptions) void tst_QArrayData::allocate_data() { QTest::addColumn("objectSize"); QTest::addColumn("alignment"); + QTest::addColumn("allocateOptions"); QTest::addColumn("isCapacityReserved"); QTest::addColumn("isSharable"); QTest::addColumn("commonEmpty"); @@ -492,22 +494,25 @@ void tst_QArrayData::allocate_data() { "void *", sizeof(void *), Q_ALIGNOF(void *) } }; - QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, true); - QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, false); + QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0); + QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable); QVERIFY(shared_empty); QVERIFY(unsharable_empty); struct { char const *description; + QArrayData::AllocateOptions allocateOptions; bool isCapacityReserved; bool isSharable; const QArrayData *commonEmpty; } options[] = { - { "Default", false, true, shared_empty }, - { "Reserved", true, true, shared_empty }, - { "Reserved | Unsharable", true, false, unsharable_empty }, - { "Unsharable", false, false, unsharable_empty }, + { "Default", QArrayData::Default, false, true, shared_empty }, + { "Reserved", QArrayData::CapacityReserved, true, true, shared_empty }, + { "Reserved | Unsharable", + QArrayData::CapacityReserved | QArrayData::Unsharable, true, false, + unsharable_empty }, + { "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty }, }; for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) @@ -517,14 +522,15 @@ void tst_QArrayData::allocate_data() + QLatin1String(": ") + QLatin1String(options[j].description))) << types[i].objectSize << types[i].alignment - << options[j].isCapacityReserved << options[j].isSharable - << options[j].commonEmpty; + << 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::AllocateOptions, allocateOptions); QFETCH(bool, isCapacityReserved); QFETCH(bool, isSharable); QFETCH(const QArrayData *, commonEmpty); @@ -535,14 +541,14 @@ void tst_QArrayData::allocate() // Shared Empty QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0, - isCapacityReserved, isSharable), commonEmpty); + QArrayData::AllocateOptions(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, isCapacityReserved, isSharable); + capacity, QArrayData::AllocateOptions(allocateOptions)); keeper.headers.append(data); QCOMPARE(data->size, 0); @@ -584,7 +590,7 @@ void tst_QArrayData::alignment() for (int i = 0; i < 100; ++i) { QArrayData *data = QArrayData::allocate(sizeof(Unaligned), - minAlignment, 8, false, true); + minAlignment, 8, QArrayData::Default); keeper.headers.append(data); QVERIFY(data); @@ -952,10 +958,14 @@ void tst_QArrayData::setSharable_data() { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } }; - QArrayDataPointer emptyReserved(QTypedArrayData::allocate(5, true, true)); - QArrayDataPointer nonEmpty(QTypedArrayData::allocate(10, false, true)); - QArrayDataPointer nonEmptyReserved(QTypedArrayData::allocate(15, true, true)); - QArrayDataPointer staticArray(static_cast *>(&staticArrayData.header)); + QArrayDataPointer emptyReserved(QTypedArrayData::allocate(5, + QArrayData::CapacityReserved)); + QArrayDataPointer nonEmpty(QTypedArrayData::allocate(10, + QArrayData::Default)); + QArrayDataPointer nonEmptyReserved(QTypedArrayData::allocate(15, + QArrayData::CapacityReserved)); + QArrayDataPointer staticArray( + static_cast *>(&staticArrayData.header)); nonEmpty->copyAppend(5, 1); nonEmptyReserved->copyAppend(7, 2); From cb0cdf6c0835c7cdff341c285cd0708aa2f4cb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Mon, 31 Oct 2011 15:30:20 +0100 Subject: [PATCH 18/74] Use RefCount::setSharable feature in QVector Note: This constitutes a break in Binary Compatibility. Change-Id: I050587901725b701f20dd46475ae48aec28aa54d Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qvector.cpp | 2 +- src/corelib/tools/qvector.h | 60 +++++++++++-------- .../corelib/tools/qvector/qrawvector.h | 1 - 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 95ae76eb1e..d4c7fd79f7 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -52,7 +52,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, true, false, 0 }; +const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, 0 }; QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) { diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index f408f6571f..f9db421fdb 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -70,13 +70,11 @@ struct Q_CORE_EXPORT QVectorData int size; #if defined(QT_ARCH_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) // workaround for bug in gcc 3.4.2 - uint sharable; uint capacity; uint reserved; #else - uint sharable : 1; uint capacity : 1; - uint reserved : 30; + uint reserved : 31; #endif static const QVectorData shared_null; @@ -120,7 +118,19 @@ public: inline QVector() : d(const_cast(&QVectorData::shared_null)) { } explicit QVector(int size); QVector(int size, const T &t); - inline QVector(const QVector &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + inline QVector(const QVector &v) + { + if (v.d->ref.ref()) { + d = v.d; + } else { + d = const_cast(&QVectorData::shared_null); + realloc(0, v.d->alloc); + qCopy(v.p->array, v.p->array + v.d->size, p->array); + d->size = v.d->size; + d->capacity = v.d->capacity; + } + } + inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } QVector &operator=(const QVector &v); #ifdef Q_COMPILER_RVALUE_REFS @@ -144,9 +154,18 @@ public: void reserve(int size); inline void squeeze() { realloc(d->size, d->size); d->capacity = 0; } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QVectorData::shared_null) d->sharable = sharable; } + inline void detach() { if (!isDetached()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != &QVectorData::shared_null) + d->ref.setSharable(sharable); + } + inline bool isSharedWith(const QVector &other) const { return d == other.d; } inline T *data() { detach(); return p->array; } @@ -337,7 +356,7 @@ void QVector::detach_helper() { realloc(d->size, d->alloc); } template void QVector::reserve(int asize) -{ if (asize > d->alloc) realloc(d->size, asize); if (d->ref == 1) d->capacity = 1; } +{ if (asize > d->alloc) realloc(d->size, asize); if (isDetached()) d->capacity = 1; } template void QVector::resize(int asize) { realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? @@ -389,13 +408,10 @@ inline void QVector::replace(int i, const T &t) template QVector &QVector::operator=(const QVector &v) { - QVectorData *o = v.d; - o->ref.ref(); - if (!d->ref.deref()) - free(p); - d = o; - if (!d->sharable) - detach_helper(); + if (v.d != d) { + QVector tmp(v); + tmp.swap(*this); + } return *this; } @@ -413,7 +429,6 @@ QVector::QVector(int asize) d = malloc(asize); d->ref.initializeOwned(); d->alloc = d->size = asize; - d->sharable = true; d->capacity = false; if (QTypeInfo::isComplex) { T* b = p->array; @@ -431,7 +446,6 @@ QVector::QVector(int asize, const T &t) d = malloc(asize); d->ref.initializeOwned(); d->alloc = d->size = asize; - d->sharable = true; d->capacity = false; T* i = p->array + d->size; while (i != p->array) @@ -445,7 +459,6 @@ QVector::QVector(std::initializer_list args) d = malloc(int(args.size())); d->ref.initializeOwned(); d->alloc = d->size = int(args.size()); - d->sharable = true; d->capacity = false; T* i = p->array + d->size; auto it = args.end(); @@ -477,7 +490,7 @@ void QVector::realloc(int asize, int aalloc) union { QVectorData *d; Data *p; } x; x.d = d; - if (QTypeInfo::isComplex && asize < d->size && d->ref == 1 ) { + if (QTypeInfo::isComplex && asize < d->size && isDetached()) { // call the destructor on all objects that need to be // destroyed when shrinking pOld = p->array + d->size; @@ -488,13 +501,13 @@ void QVector::realloc(int asize, int aalloc) } } - if (aalloc != d->alloc || d->ref != 1) { + if (aalloc != d->alloc || !isDetached()) { // (re)allocate memory if (QTypeInfo::isStatic) { x.d = malloc(aalloc); Q_CHECK_PTR(x.p); x.d->size = 0; - } else if (d->ref != 1) { + } else if (!isDetached()) { x.d = malloc(aalloc); Q_CHECK_PTR(x.p); if (QTypeInfo::isComplex) { @@ -517,7 +530,6 @@ void QVector::realloc(int asize, int aalloc) } x.d->ref.initializeOwned(); x.d->alloc = aalloc; - x.d->sharable = true; x.d->capacity = d->capacity; x.d->reserved = 0; } @@ -572,7 +584,7 @@ Q_OUTOFLINE_TEMPLATE T QVector::value(int i, const T &defaultValue) const template void QVector::append(const T &t) { - if (d->ref != 1 || d->size + 1 > d->alloc) { + if (!isDetached() || d->size + 1 > d->alloc) { const T copy(t); realloc(d->size, (d->size + 1 > d->alloc) ? QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo::isStatic) @@ -596,7 +608,7 @@ Q_TYPENAME QVector::iterator QVector::insert(iterator before, size_type n, int offset = int(before - p->array); if (n != 0) { const T copy(t); - if (d->ref != 1 || d->size + n > d->alloc) + if (!isDetached() || d->size + n > d->alloc) realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), QTypeInfo::isStatic)); if (QTypeInfo::isStatic) { diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index f786d83a53..2cdabb30c5 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -292,7 +292,6 @@ public: d->ref.initializeOwned(); d->alloc = m_alloc; d->size = m_size; - d->sharable = 0; d->capacity = 0; QVector v; From 25b8b2437ca4dc2d77ab985f491867bdbe2fff32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 25 Nov 2011 14:12:54 +0100 Subject: [PATCH 19/74] Add setSharable support to SimpleVector Change-Id: I606064d86b58be1a6a57f64f4eb55a4a751a0811 Reviewed-by: Thiago Macieira --- .../corelib/tools/qarraydata/simplevector.h | 11 +-- .../tools/qarraydata/tst_qarraydata.cpp | 72 +++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 4f02df1c40..e7032f0608 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -89,6 +89,9 @@ public: bool isStatic() const { return d->ref.isStatic(); } bool isShared() const { return d->ref.isShared(); } bool isSharedWith(const SimpleVector &other) const { return d == other.d; } + bool isSharable() const { return d->ref.isSharable(); } + + void setSharable(bool sharable) { d.setSharable(sharable); } size_t size() const { return d->size; } size_t capacity() const { return d->alloc; } @@ -139,7 +142,7 @@ public: if (n > capacity() || (n && !d->capacityReserved - && (d->ref != 1 || (d->capacityReserved = 1, false)))) { + && (d->ref.isShared() || (d->capacityReserved = 1, false)))) { SimpleVector detached(Data::allocate(n, d->detachFlags() | Data::CapacityReserved)); detached.d->copyAppend(constBegin(), constEnd()); @@ -158,7 +161,7 @@ public: return; T *const begin = d->begin(); - if (d->ref != 1 + if (d->ref.isShared() || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), @@ -179,7 +182,7 @@ public: if (first == last) return; - if (d->ref != 1 + if (d->ref.isShared() || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), @@ -219,7 +222,7 @@ public: T *const begin = d->begin(); T *const where = begin + position; const T *const end = begin + d->size; - if (d->ref != 1 + if (d->ref.isShared() || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 2df4131f4a..90c865c9e7 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -292,6 +292,15 @@ void tst_QArrayData::simpleVector() QVERIFY(!v7.isShared()); QVERIFY(!v8.isShared()); + QVERIFY(v1.isSharable()); + QVERIFY(v2.isSharable()); + QVERIFY(v3.isSharable()); + QVERIFY(v4.isSharable()); + QVERIFY(v5.isSharable()); + QVERIFY(v6.isSharable()); + QVERIFY(v7.isSharable()); + QVERIFY(v8.isSharable()); + QVERIFY(v1.isSharedWith(v2)); QVERIFY(v1.isSharedWith(v3)); QVERIFY(!v1.isSharedWith(v4)); @@ -451,6 +460,69 @@ void tst_QArrayData::simpleVector() for (int i = 0; i < 120; ++i) QCOMPARE(v1[i], v8[i % 10]); + + { + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector 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 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 copy2(v7); + QVERIFY(copy2.isSharedWith(v7)); + } + + { + SimpleVector null; + SimpleVector 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()); + } } struct Deallocator From 5a92bc9760eb0bff73ac312850f81059f05eb5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 6 Jan 2012 14:38:57 +0100 Subject: [PATCH 20/74] Don't allocate when inserting overlapping data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (This is only for a test case, but still...) Change-Id: Ied205860e5469000249e15a5478c10db53f1fdaa Reviewed-by: Thiago Macieira Reviewed-by: Jędrzej Nowacki --- tests/auto/corelib/tools/qarraydata/simplevector.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index e7032f0608..54c5fd2f61 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -237,11 +237,15 @@ public: return; } - // Temporarily copy overlapping data, if needed if ((first >= where && first < end) || (last > where && last <= end)) { - SimpleVector tmp(first, last); - d->insert(where, tmp.constBegin(), tmp.constEnd()); + // Copy overlapping data first and only then shuffle it into place + T *start = d->begin() + position; + T *middle = d->end(); + + d->copyAppend(first, last); + std::rotate(start, middle, d->end()); + return; } From 2c52e9a5c1d6ef6cbf4577430e14027375465c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 10 Jan 2012 16:03:30 +0100 Subject: [PATCH 21/74] Expand if condition for readability Change-Id: I5057c236457587ad03b55019cb340cf59d9ecdb5 Reviewed-by: Thiago Macieira --- .../corelib/tools/qarraydata/simplevector.h | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 54c5fd2f61..a1eb2dac48 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -139,15 +139,22 @@ public: void reserve(size_t n) { - if (n > capacity() - || (n - && !d->capacityReserved - && (d->ref.isShared() || (d->capacityReserved = 1, false)))) { - SimpleVector detached(Data::allocate(n, - d->detachFlags() | Data::CapacityReserved)); - detached.d->copyAppend(constBegin(), constEnd()); - detached.swap(*this); + if (n == 0) + return; + + if (n <= capacity()) { + if (d->capacityReserved) + return; + if (!d->ref.isShared()) { + d->capacityReserved = 1; + return; + } } + + SimpleVector detached(Data::allocate(n, + d->detachFlags() | Data::CapacityReserved)); + detached.d->copyAppend(constBegin(), constEnd()); + detached.swap(*this); } void prepend(const_iterator first, const_iterator last) From f4c1e2c40fcc1285ea24d55ed4eac036d8bbdf1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 11 Jan 2012 17:16:04 +0100 Subject: [PATCH 22/74] Enable QArrayData to reference external array data By default, QTypedArrayData::fromRawData provides the same semantics as already exist in QByteArray and QString (immutable, sharable data), but more combinations are possible. In particular, immutable-unsharable leaves the data owner in control of its lifetime by forcing deep copies. As part of this, a new isMutable property is introduced in QArrayData. This could be taken to be implicit in statics that are initialized with a proper size but with alloc set to 0. QStringLiteral and QByteLiteral already did this, forcing re-allocations on resize even before the (static, thus shared) ref-count is considered. The isMutable property detaches data mutability and shared status, which are orthogonal concepts (at least in the unshared state). For the time being, there is no API to explicitly (re)set mutability, but statics and RawData mark data immutable. Change-Id: I33a995a35e1c3d7a12391b1d7c36095aa28e221a Reviewed-by: Robin Burchell Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.cpp | 9 ++-- src/corelib/tools/qarraydata.h | 23 +++++++++ src/corelib/tools/qarraydatapointer.h | 4 +- .../corelib/tools/qarraydata/simplevector.h | 6 +++ .../tools/qarraydata/tst_qarraydata.cpp | 51 +++++++++++++++++++ 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index efed984aef..8f0a95c82c 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -56,16 +56,19 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, && !(alignment & (alignment - 1))); // Don't allocate empty headers - if (!capacity) + if (!(options & RawData) && !capacity) return !(options & Unsharable) ? const_cast(&qt_array_empty) : const_cast(&qt_array_unsharable_empty); + size_t allocSize = sizeof(QArrayData) + objectSize * capacity; + // 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! - size_t allocSize = sizeof(QArrayData) + objectSize * capacity - + (alignment - Q_ALIGNOF(QArrayData)); + // Padding is skipped when allocating a header for RawData. + if (!(options & RawData)) + allocSize += (alignment - Q_ALIGNOF(QArrayData)); QArrayData *header = static_cast(qMalloc(allocSize)); Q_CHECK_PTR(header); diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 8eb543ee51..5a17d718c9 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -73,9 +73,18 @@ struct Q_CORE_EXPORT QArrayData return reinterpret_cast(this) + offset; } + // 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 alloc != 0; + } + enum AllocateOption { CapacityReserved = 0x1, Unsharable = 0x2, + RawData = 0x4, Default = 0 }; @@ -139,6 +148,20 @@ struct QTypedArrayData QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy)); } + static QTypedArrayData *fromRawData(const T *data, size_t n, + AllocateOptions options = Default) + { + QTypedArrayData *result = allocate(0, options | RawData); + if (result) { + Q_ASSERT(!result->ref.isShared()); // No shared empty, please! + + result->offset = reinterpret_cast(data) + - reinterpret_cast(result); + result->size = n; + } + return result; + } + static QTypedArrayData *sharedNull() { return static_cast( diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 1dc02daa63..8b1d2a805c 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -114,6 +114,8 @@ public: void setSharable(bool sharable) { + // Can't call setSharable on static read-only data, like shared_null + // and the internal shared-empties. if (d->alloc == 0 && d->size == 0) { d = Data::allocate(0, sharable ? QArrayData::Default @@ -138,7 +140,7 @@ public: bool detach() { - if (d->ref.isShared()) { + if (!d->isMutable() || d->ref.isShared()) { Data *copy = clone(d->detachFlags()); QArrayDataPointer old(d); d = copy; diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index a1eb2dac48..3fa7905a39 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -274,6 +274,12 @@ public: d.detach(); } + static SimpleVector fromRawData(const T *data, size_t size, + QArrayData::AllocateOptions options = Data::Default) + { + return SimpleVector(Data::fromRawData(data, size, options)); + } + private: QArrayDataPointer d; }; diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 90c865c9e7..4dba02cc70 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -81,6 +81,7 @@ private slots: void arrayOps(); void setSharable_data(); void setSharable(); + void fromRawData(); }; template const T &const_(const T &t) { return t; } @@ -1131,5 +1132,55 @@ void tst_QArrayData::setSharable() QVERIFY(array->ref.isSharable()); } +void tst_QArrayData::fromRawData() +{ + static const int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + { + // Default: Immutable, sharable + SimpleVector raw = SimpleVector::fromRawData(array, + sizeof(array)/sizeof(array[0]), QArrayData::Default); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0])); + + QVERIFY(!raw.isShared()); + QVERIFY(SimpleVector(raw).isSharedWith(raw)); + QVERIFY(!raw.isShared()); + + // Detach + QCOMPARE(raw.back(), 11); + QVERIFY(raw.constBegin() != array); + } + + { + // Immutable, unsharable + SimpleVector raw = SimpleVector::fromRawData(array, + sizeof(array)/sizeof(array[0]), QArrayData::Unsharable); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0])); + + SimpleVector 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(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0])); + + // Detach + QCOMPARE(raw.back(), 11); + QVERIFY(raw.constBegin() != array); + } +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From b3a4d3e328a3d80d4728716f2e5e68817b82cbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 12 Jan 2012 16:08:54 +0100 Subject: [PATCH 23/74] Rename QArrayData::AllocateOption to AllocationOption Change-Id: Id3e7c748b4b40d703ad1785c903c96bdd968390e Reviewed-by: Oswald Buddenhagen --- src/corelib/tools/qarraydata.cpp | 2 +- src/corelib/tools/qarraydata.h | 21 ++++++++++--------- src/corelib/tools/qarraydatapointer.h | 2 +- .../corelib/tools/qarraydata/simplevector.h | 2 +- .../tools/qarraydata/tst_qarraydata.cpp | 12 +++++------ 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 8f0a95c82c..6a5632a47f 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -49,7 +49,7 @@ static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0 static const QArrayData qt_array_unsharable_empty = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, - size_t capacity, AllocateOptions options) + size_t capacity, AllocationOptions options) { // Alignment is a power of two Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 5a17d718c9..c022d9f302 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -81,7 +81,7 @@ struct Q_CORE_EXPORT QArrayData return alloc != 0; } - enum AllocateOption { + enum AllocationOption { CapacityReserved = 0x1, Unsharable = 0x2, RawData = 0x4, @@ -89,11 +89,11 @@ struct Q_CORE_EXPORT QArrayData Default = 0 }; - Q_DECLARE_FLAGS(AllocateOptions, AllocateOption) + Q_DECLARE_FLAGS(AllocationOptions, AllocationOption) - AllocateOptions detachFlags() const + AllocationOptions detachFlags() const { - AllocateOptions result; + AllocationOptions result; if (!ref.isSharable()) result |= Unsharable; if (capacityReserved) @@ -101,23 +101,24 @@ struct Q_CORE_EXPORT QArrayData return result; } - AllocateOptions cloneFlags() const + AllocationOptions cloneFlags() const { - AllocateOptions result; + AllocationOptions result; if (capacityReserved) result |= CapacityReserved; return result; } static QArrayData *allocate(size_t objectSize, size_t alignment, - size_t capacity, AllocateOptions options = Default) Q_REQUIRED_RESULT; + size_t capacity, AllocationOptions options = Default) + Q_REQUIRED_RESULT; static void deallocate(QArrayData *data, size_t objectSize, size_t alignment); static const QArrayData shared_null; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocateOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocationOptions) template struct QTypedArrayData @@ -137,7 +138,7 @@ struct QTypedArrayData class AlignmentDummy { QArrayData header; T data; }; static QTypedArrayData *allocate(size_t capacity, - AllocateOptions options = Default) Q_REQUIRED_RESULT + AllocationOptions options = Default) Q_REQUIRED_RESULT { return static_cast(QArrayData::allocate(sizeof(T), Q_ALIGNOF(AlignmentDummy), capacity, options)); @@ -149,7 +150,7 @@ struct QTypedArrayData } static QTypedArrayData *fromRawData(const T *data, size_t n, - AllocateOptions options = Default) + AllocationOptions options = Default) { QTypedArrayData *result = allocate(0, options | RawData); if (result) { diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 8b1d2a805c..1539b3672f 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -151,7 +151,7 @@ public: } private: - Data *clone(QArrayData::AllocateOptions options) const Q_REQUIRED_RESULT + Data *clone(QArrayData::AllocationOptions options) const Q_REQUIRED_RESULT { QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size, options)); diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 3fa7905a39..d53b90d8a4 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -275,7 +275,7 @@ public: } static SimpleVector fromRawData(const T *data, size_t size, - QArrayData::AllocateOptions options = Data::Default) + QArrayData::AllocationOptions options = Data::Default) { return SimpleVector(Data::fromRawData(data, size, options)); } diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 4dba02cc70..4d121a823f 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -546,13 +546,13 @@ struct Deallocator }; Q_DECLARE_METATYPE(const QArrayData *) -Q_DECLARE_METATYPE(QArrayData::AllocateOptions) +Q_DECLARE_METATYPE(QArrayData::AllocationOptions) void tst_QArrayData::allocate_data() { QTest::addColumn("objectSize"); QTest::addColumn("alignment"); - QTest::addColumn("allocateOptions"); + QTest::addColumn("allocateOptions"); QTest::addColumn("isCapacityReserved"); QTest::addColumn("isSharable"); QTest::addColumn("commonEmpty"); @@ -575,7 +575,7 @@ void tst_QArrayData::allocate_data() struct { char const *description; - QArrayData::AllocateOptions allocateOptions; + QArrayData::AllocationOptions allocateOptions; bool isCapacityReserved; bool isSharable; const QArrayData *commonEmpty; @@ -603,7 +603,7 @@ void tst_QArrayData::allocate() { QFETCH(size_t, objectSize); QFETCH(size_t, alignment); - QFETCH(QArrayData::AllocateOptions, allocateOptions); + QFETCH(QArrayData::AllocationOptions, allocateOptions); QFETCH(bool, isCapacityReserved); QFETCH(bool, isSharable); QFETCH(const QArrayData *, commonEmpty); @@ -614,14 +614,14 @@ void tst_QArrayData::allocate() // Shared Empty QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0, - QArrayData::AllocateOptions(allocateOptions)), commonEmpty); + 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::AllocateOptions(allocateOptions)); + capacity, QArrayData::AllocationOptions(allocateOptions)); keeper.headers.append(data); QCOMPARE(data->size, 0); From 301f7b780cbb0e5b1f6c9bf88bdb7dffe9b1110e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 10 Nov 2011 18:13:36 +0100 Subject: [PATCH 24/74] Don't check reference count in QLinkedList::free This is a private function that was always* called after d->ref.deref() returned false to free the linked list. Still, it needlessly verified the reference count to be zero. The check is thus replaced with a Q_ASSERT to check the invariant on debug builds. *This commit also fixes an issue where free would be called on a block that hadn't been deref'ed, thus leaking the nodes. Since this was in an exception handling block, and happens before any code has a chance to reference the block the explicit deref is skipped in that case. Change-Id: Ie73c174d0a1b84f297bf5531e45f829e66a46346 Reviewed-by: Robin Burchell Reviewed-by: Olivier Goffart Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qlinkedlist.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 966b74ddfa..279eda673a 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -266,6 +266,7 @@ void QLinkedList::detach_helper() copy = copy->n; } QT_CATCH(...) { copy->n = x.e; + Q_ASSERT(!x.d->ref.deref()); // Don't trigger assert in free free(x.d); QT_RETHROW; } @@ -282,14 +283,13 @@ void QLinkedList::free(QLinkedListData *x) { Node *y = reinterpret_cast(x); Node *i = y->n; - if (x->ref == 0) { - while(i != y) { - Node *n = i; - i = i->n; - delete n; - } - delete x; + Q_ASSERT(x->ref.atomic.load() == 0); + while (i != y) { + Node *n = i; + i = i->n; + delete n; } + delete x; } template From 9e9f7a482abbfc862c9a5cc292139a36b5f25700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 11 Jan 2012 16:01:10 +0100 Subject: [PATCH 25/74] Don't use RefCount int operations , as those are going away. This cleans use of those operations in the QArrayData stack. Change-Id: I67705fe0a2f8d99ea13739b675021356a5736f83 Reviewed-by: Robin Burchell Reviewed-by: hjk --- src/corelib/tools/qarraydataops.h | 4 +-- .../tools/qarraydata/tst_qarraydata.cpp | 36 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index a3c372fe6b..8800cff028 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -83,7 +83,7 @@ struct QPodArrayOps void destroyAll() // Call from destructors, ONLY! { - Q_ASSERT(this->ref == 0); + Q_ASSERT(this->ref.atomic.load() == 0); // As this is to be called only from destructor, it doesn't need to be // exception safe; size not updated. @@ -138,7 +138,7 @@ struct QGenericArrayOps // As this is to be called only from destructor, it doesn't need to be // exception safe; size not updated. - Q_ASSERT(this->ref == 0); + Q_ASSERT(this->ref.atomic.load() == 0); const T *const b = this->begin(); const T *i = this->end(); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 4d121a823f..d1da18d4e1 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -92,25 +92,25 @@ void tst_QArrayData::referenceCounting() // Reference counting initialized to 1 (owned) QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 }; - QCOMPARE(int(array.ref), 1); + QCOMPARE(array.ref.atomic.load(), 1); QVERIFY(!array.ref.isStatic()); QVERIFY(array.ref.isSharable()); QVERIFY(array.ref.ref()); - QCOMPARE(int(array.ref), 2); + QCOMPARE(array.ref.atomic.load(), 2); QVERIFY(array.ref.deref()); - QCOMPARE(int(array.ref), 1); + QCOMPARE(array.ref.atomic.load(), 1); QVERIFY(array.ref.ref()); - QCOMPARE(int(array.ref), 2); + QCOMPARE(array.ref.atomic.load(), 2); QVERIFY(array.ref.deref()); - QCOMPARE(int(array.ref), 1); + QCOMPARE(array.ref.atomic.load(), 1); QVERIFY(!array.ref.deref()); - QCOMPARE(int(array.ref), 0); + QCOMPARE(array.ref.atomic.load(), 0); // Now would be a good time to free/release allocated data } @@ -119,17 +119,17 @@ void tst_QArrayData::referenceCounting() // Reference counting initialized to 0 (non-sharable) QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; - QCOMPARE(int(array.ref), 0); + QCOMPARE(array.ref.atomic.load(), 0); QVERIFY(!array.ref.isStatic()); QVERIFY(!array.ref.isSharable()); QVERIFY(!array.ref.ref()); // Reference counting fails, data should be copied - QCOMPARE(int(array.ref), 0); + QCOMPARE(array.ref.atomic.load(), 0); QVERIFY(!array.ref.deref()); - QCOMPARE(int(array.ref), 0); + QCOMPARE(array.ref.atomic.load(), 0); // Free/release data } @@ -138,16 +138,16 @@ void tst_QArrayData::referenceCounting() // Reference counting initialized to -1 (static read-only data) QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; - QCOMPARE(int(array.ref), -1); + QCOMPARE(array.ref.atomic.load(), -1); QVERIFY(array.ref.isStatic()); QVERIFY(array.ref.isSharable()); QVERIFY(array.ref.ref()); - QCOMPARE(int(array.ref), -1); + QCOMPARE(array.ref.atomic.load(), -1); QVERIFY(array.ref.deref()); - QCOMPARE(int(array.ref), -1); + QCOMPARE(array.ref.atomic.load(), -1); } } @@ -164,20 +164,20 @@ void tst_QArrayData::sharedNullEmpty() QVERIFY(empty->ref.isSharable()); QVERIFY(empty->ref.isShared()); - QCOMPARE(int(null->ref), -1); - QCOMPARE(int(empty->ref), -1); + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); QVERIFY(null->ref.ref()); QVERIFY(empty->ref.ref()); - QCOMPARE(int(null->ref), -1); - QCOMPARE(int(empty->ref), -1); + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); QVERIFY(null->ref.deref()); QVERIFY(empty->ref.deref()); - QCOMPARE(int(null->ref), -1); - QCOMPARE(int(empty->ref), -1); + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); QVERIFY(null != empty); From e465a8c58cef94029b11bbb5e53c89833d85d96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 13 Jan 2012 16:39:10 +0100 Subject: [PATCH 26/74] Don't use qMalloc/qFree in non-inline code This propagates changes in b08daaedd45457b775cb90d2c2650510daff1c8d to this branch. Change-Id: I3b72f53c7b24d27075ea8593c347b504bfd8f581 Reviewed-by: Robin Burchell Reviewed-by: hjk --- src/corelib/tools/qarraydata.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 6a5632a47f..275fbc1ed5 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -41,6 +41,8 @@ #include +#include + QT_BEGIN_NAMESPACE const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; @@ -70,7 +72,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, if (!(options & RawData)) allocSize += (alignment - Q_ALIGNOF(QArrayData)); - QArrayData *header = static_cast(qMalloc(allocSize)); + QArrayData *header = static_cast(::malloc(allocSize)); Q_CHECK_PTR(header); if (header) { quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) @@ -97,7 +99,7 @@ void QArrayData::deallocate(QArrayData *data, size_t objectSize, if (data == &qt_array_unsharable_empty) return; - qFree(data); + ::free(data); } QT_END_NAMESPACE From 66f192e29478ea586ff14741a734e590375d5bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 13 Jan 2012 16:51:07 +0100 Subject: [PATCH 27/74] Move Q_CHECK_PTR to inline code The behavior of Q_CHECK_PTR is user-configurable. It may throw and exception, output a warning or be a no-op. As such, its best used in inline code that gets compiled into the user application. Moving it out of the QArrayData API also enables this to be used in code that must handle out-of-memory errors without crashing or otherwise issuing warnings. Putting in QArrayDataPointer gives good convenience coverage for those who are implementing containers on top of the QArrayData stack, and covers its use in the SimpleVector test case. It intentionally leaves out the use of allocate to access the (hidden) shared-empties which don't really allocate, anyway (in QArrayDataPointer::clear and setSharable). The autotest is not updated and will have to make do without the Q_CHECK_PTR "protection". I think that is ok. Change-Id: Idcad909903a9807866bf23ace89f1bf1edc53c3b Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.cpp | 1 - src/corelib/tools/qarraydatapointer.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 275fbc1ed5..a693f04042 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -73,7 +73,6 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, allocSize += (alignment - Q_ALIGNOF(QArrayData)); QArrayData *header = static_cast(::malloc(allocSize)); - Q_CHECK_PTR(header); if (header) { quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1); diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 1539b3672f..81eae4cf81 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -73,6 +73,7 @@ public: explicit QArrayDataPointer(QTypedArrayData *ptr) : d(ptr) { + Q_CHECK_PTR(ptr); } QArrayDataPointer &operator=(const QArrayDataPointer &other) From cc568ec342161edee01ecae74ac11bb4c2da46b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 10 Nov 2011 14:52:40 +0100 Subject: [PATCH 28/74] Remove obsolete function QVectorData::allocate is able to handle higher alignment that QVectorData::malloc didn't. This was kept for BC issues in the 4.x series, we can drop it now. Change-Id: I7782bd2910de6b9c8dee3e63e621629dc3889d33 Reviewed-by: Robin Burchell --- src/corelib/tools/qvector.cpp | 8 -------- src/corelib/tools/qvector.h | 4 ---- 2 files changed, 12 deletions(-) diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 59ca11179b..a7a6add15a 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -56,14 +56,6 @@ static inline int alignmentThreshold() const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, 0 }; -QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) -{ - QVectorData* p = (QVectorData *)::malloc(sizeofTypedData + (size - 1) * sizeofT); - Q_CHECK_PTR(p); - ::memcpy(p, init, sizeofTypedData + (qMin(size, init->alloc) - 1) * sizeofT); - return p; -} - QVectorData *QVectorData::allocate(int size, int alignment) { return static_cast(alignment > alignmentThreshold() ? qMallocAligned(size, alignment) : ::malloc(size)); diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 51364df91d..0116d1db6c 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -78,10 +78,6 @@ struct Q_CORE_EXPORT QVectorData #endif static const QVectorData shared_null; - // ### Qt 5: rename to 'allocate()'. The current name causes problems for - // some debugges when the QVector is member of a class within an unnamed namespace. - // ### Qt 5: can be removed completely. (Ralf) - static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init); static QVectorData *allocate(int size, int alignment); static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); static void free(QVectorData *data, int alignment); From 3249aab539b37a98d75329598bd6838082a8158e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 12 Jan 2012 15:39:17 +0100 Subject: [PATCH 29/74] Don't use RefCount int operators , as those are going away. The comment in QString/QByteArray::squeeze about shared_null was updated as it also affects other static data, such as that generated by QStringLiteral and QByteArrayLiteral. Change-Id: I26a757d29db62b1e3566a1f7c8d4030918ed8a89 Reviewed-by: Thiago Macieira Reviewed-by: Bradley T. Hughes Reviewed-by: Olivier Goffart --- src/corelib/tools/qbytearray.cpp | 22 +++++++++---------- src/corelib/tools/qbytearray.h | 11 +++++----- src/corelib/tools/qhash.h | 4 ++-- src/corelib/tools/qlinkedlist.h | 4 ++-- src/corelib/tools/qlist.cpp | 16 +++++++------- src/corelib/tools/qlist.h | 16 +++++++------- src/corelib/tools/qmap.h | 4 ++-- src/corelib/tools/qstring.cpp | 14 ++++++------ src/corelib/tools/qstring.h | 13 ++++++----- .../tools/qbytearray/tst_qbytearray.cpp | 2 +- 10 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 47bf5da619..4173cf28e2 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -904,7 +904,7 @@ QByteArray &QByteArray::operator=(const char *str) x = const_cast(&shared_empty.ba); } else { int len = qstrlen(str); - if (d->ref != 1 || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) + if (d->ref.isShared() || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) realloc(len); x = d; memcpy(x->data(), str, len + 1); // include null terminator @@ -1403,7 +1403,7 @@ void QByteArray::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (d->offset && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1432,7 +1432,7 @@ void QByteArray::resize(int size) x->data()[size] = '\0'; d = x; } else { - if (d->ref != 1 || size > int(d->alloc) + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(qAllocMore(size, sizeof(Data))); if (int(d->alloc) >= size) { @@ -1463,7 +1463,7 @@ QByteArray &QByteArray::fill(char ch, int size) void QByteArray::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || d->offset) { Data *x = static_cast(malloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->ref.initializeOwned(); @@ -1564,7 +1564,7 @@ QByteArray &QByteArray::prepend(const char *str) QByteArray &QByteArray::prepend(const char *str, int len) { if (str) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memmove(d->data()+len, d->data(), d->size); memcpy(d->data(), str, len); @@ -1582,7 +1582,7 @@ QByteArray &QByteArray::prepend(const char *str, int len) QByteArray &QByteArray::prepend(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); memmove(d->data()+1, d->data(), d->size); d->data()[0] = ch; @@ -1620,7 +1620,7 @@ QByteArray &QByteArray::append(const QByteArray &ba) if ((d == &shared_null.ba || d == &shared_empty.ba) && !IS_RAW_DATA(ba.d)) { *this = ba; } else if (ba.d != &shared_null.ba) { - if (d->ref != 1 || d->size + ba.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + ba.d->size > int(d->alloc)) realloc(qAllocMore(d->size + ba.d->size, sizeof(Data))); memcpy(d->data() + d->size, ba.d->data(), ba.d->size); d->size += ba.d->size; @@ -1654,7 +1654,7 @@ QByteArray& QByteArray::append(const char *str) { if (str) { int len = qstrlen(str); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len + 1); // include null terminator d->size += len; @@ -1679,7 +1679,7 @@ QByteArray &QByteArray::append(const char *str, int len) if (len < 0) len = qstrlen(str); if (str && len) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len); // include null terminator d->size += len; @@ -1696,7 +1696,7 @@ QByteArray &QByteArray::append(const char *str, int len) QByteArray& QByteArray::append(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); d->data()[d->size++] = ch; d->data()[d->size] = '\0'; @@ -3912,7 +3912,7 @@ QByteArray QByteArray::fromRawData(const char *data, int size) */ QByteArray &QByteArray::setRawData(const char *data, uint size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(data, size); } else { if (data) { diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 2409ff1232..bf75ef6ead 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -429,9 +429,9 @@ inline const char *QByteArray::data() const inline const char *QByteArray::constData() const { return d->data(); } inline void QByteArray::detach() -{ if (d->ref != 1 || d->offset) realloc(d->size); } +{ if (d->ref.isShared() || d->offset) realloc(d->size); } inline bool QByteArray::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QByteArray::QByteArray(const QByteArray &a) : d(a.d) { d->ref.ref(); } @@ -440,7 +440,7 @@ inline int QByteArray::capacity() const inline void QByteArray::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -451,11 +451,12 @@ inline void QByteArray::reserve(int asize) inline void QByteArray::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(d->size); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 0fdb1ad794..ecd02008e2 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -289,8 +289,8 @@ public: void reserve(int size); inline void squeeze() { reserve(1); } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QHashData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QHash &other) const { return d == other.d; } diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 279eda673a..e8696eb5f9 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -95,8 +95,8 @@ public: inline int size() const { return d->size; } inline void detach() - { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QLinkedListData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QLinkedList &other) const { return d == other.d; } diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 2afe1ab969..8eb4be090a 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -146,7 +146,7 @@ QListData::Data *QListData::detach(int alloc) void QListData::realloc(int alloc) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); Data *x = static_cast(::realloc(d, DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(x); @@ -159,7 +159,7 @@ void QListData::realloc(int alloc) // ensures that enough space is available to append n elements void **QListData::append(int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int e = d->end; if (e + n > d->alloc) { int b = d->begin; @@ -190,7 +190,7 @@ void **QListData::append(const QListData& l) void **QListData::prepend() { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (d->begin == 0) { if (d->end >= d->alloc / 3) realloc(grow(d->alloc + 1)); @@ -208,7 +208,7 @@ void **QListData::prepend() void **QListData::insert(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (i <= 0) return prepend(); int size = d->end - d->begin; @@ -247,7 +247,7 @@ void **QListData::insert(int i) void QListData::remove(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; if (i - d->begin < d->end - i) { if (int offset = i - d->begin) @@ -262,7 +262,7 @@ void QListData::remove(int i) void QListData::remove(int i, int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; int middle = i + n/2; if (middle - d->begin < d->end - middle) { @@ -278,7 +278,7 @@ void QListData::remove(int i, int n) void QListData::move(int from, int to) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (from == to) return; @@ -318,7 +318,7 @@ void QListData::move(int from, int to) void **QListData::erase(void **xi) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int i = xi - (d->array + d->begin); remove(i); return d->array + d->begin + i; diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index d192d31234..c3db56d686 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -133,16 +133,16 @@ public: inline int size() const { return p.size(); } - inline void detach() { if (d->ref != 1) detach_helper(); } + inline void detach() { if (d->ref.isShared()) detach_helper(); } inline void detachShared() { // The "this->" qualification is needed for GCCE. - if (d->ref != 1 && this->d != &QListData::shared_null) + if (d->ref.isShared() && this->d != &QListData::shared_null) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QListData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QList &other) const { return d == other.d; } @@ -479,7 +479,7 @@ template Q_OUTOFLINE_TEMPLATE void QList::reserve(int alloc) { if (d->alloc < alloc) { - if (d->ref != 1) + if (d->ref.isShared()) detach_helper(alloc); else p.realloc(alloc); @@ -489,7 +489,7 @@ Q_OUTOFLINE_TEMPLATE void QList::reserve(int alloc) template Q_OUTOFLINE_TEMPLATE void QList::append(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(INT_MAX, 1); QT_TRY { node_construct(n, t); @@ -523,7 +523,7 @@ Q_OUTOFLINE_TEMPLATE void QList::append(const T &t) template inline void QList::prepend(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(0, 1); QT_TRY { node_construct(n, t); @@ -557,7 +557,7 @@ inline void QList::prepend(const T &t) template inline void QList::insert(int i, const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(i, 1); QT_TRY { node_construct(n, t); @@ -803,7 +803,7 @@ Q_OUTOFLINE_TEMPLATE QList &QList::operator+=(const QList &l) if (isEmpty()) { *this = l; } else { - Node *n = (d->ref != 1) + Node *n = (d->ref.isShared()) ? detach_helper_grow(INT_MAX, l.size()) : reinterpret_cast(p.append(l.p)); QT_TRY { diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index f975b7eb16..7e19760c5f 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -200,8 +200,8 @@ public: inline bool isEmpty() const { return d->size == 0; } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QMapData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QMap &other) const { return d == other.d; } inline void setInsertInOrder(bool ordered) { if (ordered) detach(); if (d != &QMapData::shared_null) d->insertInOrder = ordered; } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index dbe1e913c3..8e49106f1d 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1237,7 +1237,7 @@ void QString::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (d->offset && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1248,7 +1248,7 @@ void QString::resize(int size) QString::free(d); d = x; } else { - if (d->ref != 1 || size > int(d->alloc) || + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(grow(size)); if (int(d->alloc) >= size) { @@ -1311,7 +1311,7 @@ void QString::resize(int size) // ### Qt 5: rename reallocData() to avoid confusion. 197625 void QString::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || d->offset) { Data *x = static_cast(::malloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); x->ref.initializeOwned(); @@ -1541,7 +1541,7 @@ QString &QString::append(const QString &str) if (d == &shared_null.str) { operator=(str); } else { - if (d->ref != 1 || d->size + str.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + str.d->size > int(d->alloc)) realloc(grow(d->size + str.d->size)); memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar)); d->size += str.d->size; @@ -1561,7 +1561,7 @@ QString &QString::append(const QLatin1String &str) const uchar *s = (const uchar *)str.latin1(); if (s) { int len = qstrlen((char *)s); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(grow(d->size + len)); ushort *i = d->data() + d->size; while ((*i++ = *s++)) @@ -1604,7 +1604,7 @@ QString &QString::append(const QLatin1String &str) */ QString &QString::append(QChar ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = ch.unicode(); d->data()[d->size] = '\0'; @@ -7090,7 +7090,7 @@ QString QString::fromRawData(const QChar *unicode, int size) */ QString &QString::setRawData(const QChar *unicode, int size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(unicode, size); } else { if (unicode) { diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index f7898bbadb..42fe85e81f 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -339,7 +339,7 @@ public: inline QString &prepend(const QLatin1String &s) { return insert(0, s); } inline QString &operator+=(QChar c) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = c.unicode(); d->data()[d->size] = '\0'; @@ -706,9 +706,9 @@ inline QChar *QString::data() inline const QChar *QString::constData() const { return reinterpret_cast(d->data()); } inline void QString::detach() -{ if (d->ref != 1 || d->offset) realloc(); } +{ if (d->ref.isShared() || d->offset) realloc(); } inline bool QString::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QString &QString::operator=(const QLatin1String &s) { *this = fromLatin1(s.latin1()); @@ -871,7 +871,7 @@ inline QString::~QString() { if (!d->ref.deref()) free(d); } inline void QString::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -882,11 +882,12 @@ inline void QString::reserve(int asize) inline void QString::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp index 8767571f6c..5b660ecd3f 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -1504,7 +1504,7 @@ void tst_QByteArray::literals() QVERIFY(str.length() == 4); QVERIFY(str == "abcd"); - QVERIFY(str.data_ptr()->ref == -1); + QVERIFY(str.data_ptr()->ref.isStatic()); QVERIFY(str.data_ptr()->offset == 0); const char *s = str.constData(); From 32240022731f513fc080a6ca3e7ba6a2374fe0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 24 Jan 2012 13:51:41 +0100 Subject: [PATCH 30/74] Don't use RefCount int operators The previous patch missed this, which was hidden inside #ifdefs. Change-Id: Iba1417d4191b6931afb549022768f3e3a2cf727d Reviewed-by: Lars Knoll --- tests/auto/corelib/tools/qstring/tst_qstring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index ed525e3429..622d494a06 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -5120,7 +5120,7 @@ void tst_QString::literals() QVERIFY(str.length() == 4); QVERIFY(str == QLatin1String("abcd")); - QVERIFY(str.data_ptr()->ref == -1); + QVERIFY(str.data_ptr()->ref.isStatic()); QVERIFY(str.data_ptr()->offset == 0); const QChar *s = str.constData(); From d8ff456b4d762d18d0e3a3b97bb897a826667d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 10 Nov 2011 18:19:19 +0100 Subject: [PATCH 31/74] Removed remaining int operations from RefCount RefCount has currently three magic values (-1 for statics, 0 for unsharables, 1 for unshared-sharable). From the API point of view, the individual values are not important or necessary and it's error prone for users of the class to be fiddling with them. Change-Id: I7037cc31d3d062abbad73d04962bc74d85ebd20f Reviewed-by: Robin Burchell Reviewed-by: Olivier Goffart --- src/corelib/tools/qrefcount.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 698351456f..1d2474b6d8 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -101,15 +101,6 @@ public: return (count != 1) && (count != 0); } - inline bool operator==(int value) const - { return atomic.load() == value; } - inline bool operator!=(int value) const - { return atomic.load() != value; } - inline bool operator!() const - { return !atomic.load(); } - inline operator int() const - { return atomic.load(); } - void initializeOwned() { atomic.store(1); } void initializeUnsharable() { atomic.store(0); } From f218213a42f4d1b1bbf291ee27730f83cfeed6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 3 Nov 2011 16:04:32 +0100 Subject: [PATCH 32/74] Enable use of QArrayDataPointer as a return type This is done through a (POD) wrapper around a raw QTypedArrayData pointer. The wrapper forces conscious decisions on the use of that pointer, while implying it is internal API. The pointed-to header is assumed to referenced, as if already owned by the receiving QArrayDataPointer. This wrapper is placed in the generic qarraydata.h header as its definition doesn't depend on QArrayDataPointer by itself. Change-Id: I9b2b6bb7dd9ab073dc7d882ccf999b32d793ed3f Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.h | 7 +++++++ src/corelib/tools/qarraydatapointer.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index c022d9f302..5ed8d4ba95 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -177,6 +177,13 @@ struct QStaticArrayData T data[N]; }; +// Support for returning QArrayDataPointer from functions +template +struct QArrayDataPointerRef +{ + QTypedArrayData *ptr; +}; + #define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) { \ Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, \ (sizeof(QArrayData) + (Q_ALIGNOF(type) - 1)) \ diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 81eae4cf81..8b5752bc70 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -76,6 +76,11 @@ public: Q_CHECK_PTR(ptr); } + QArrayDataPointer(QArrayDataPointerRef ref) + : d(ref.ptr) + { + } + QArrayDataPointer &operator=(const QArrayDataPointer &other) { QArrayDataPointer tmp(other); From b59d8319806ff0ed2e340126fd110413301a833b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 2 Nov 2011 13:11:31 +0100 Subject: [PATCH 33/74] Introducing Q_ARRAY_LITERAL This provides the same functionality as the specialized QStringLiteral and QByteArrayLiteral, but on top of QArrayData. The macro has two variations, variadic and simple. The variadic version depends on compiler support for (C99) variadic macros and enables static initialization of arrays of any POD data. Use of this macro is not recommended on code or applications that need to work in configurations where variadic macros are not supported. The simple version is more portable and is enough to support the use cases of QStringLiteral and QByteArrayLiteral, also providing a fallback that allocates and copies data when static initialization is not available. Change-Id: I7154a24dcae4bbbd7d5978653f620138467830c5 Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.h | 79 ++++++++++++++++ .../corelib/tools/qarraydata/simplevector.h | 5 + .../tools/qarraydata/tst_qarraydata.cpp | 92 +++++++++++++++++++ 3 files changed, 176 insertions(+) diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 5ed8d4ba95..d5d96efc08 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -43,6 +43,7 @@ #define QARRAYDATA_H #include +#include QT_BEGIN_HEADER @@ -190,6 +191,84 @@ struct QArrayDataPointerRef & ~(Q_ALIGNOF(type) - 1) } \ /**/ +//////////////////////////////////////////////////////////////////////////////// +// 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. + +#if defined(Q_COMPILER_VARIADIC_MACROS) +#if defined(Q_COMPILER_LAMBDA) +// Hide array inside a lambda +#define Q_ARRAY_LITERAL(Type, ...) \ + ([]() -> QArrayDataPointerRef { \ + /* 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 get() \ + { \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + return ref; \ + } \ + }; \ + return StaticWrapper::get(); \ + }()) \ + /**/ +#elif defined(Q_CC_GNU) +// Hide array within GCC's __extension__ {( )} block +#define Q_ARRAY_LITERAL(Type, ...) \ + __extension__ ({ \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + ref; \ + }) \ + /**/ +#endif +#endif // defined(Q_COMPILER_VARIADIC_MACROS) + +#if defined(Q_ARRAY_LITERAL) +#define Q_ARRAY_LITERAL_IMPL(Type, ...) \ + union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \ + \ + /* Portable compile-time array size computation */ \ + Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \ + enum { Size = sizeof(data) / sizeof(data[0]) }; \ + \ + static const QStaticArrayData literal = { \ + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(Type, Size), { __VA_ARGS__ } }; \ + \ + QArrayDataPointerRef ref = \ + { static_cast *>( \ + const_cast(&literal.header)) }; \ + /**/ +#else +// As a fallback, memory is allocated and data copied to the heap. + +// The fallback macro does NOT use variadic macros and does NOT support +// variable number of arguments. It is suitable for char arrays. + +namespace QtPrivate { + template + inline QArrayDataPointerRef qMakeArrayLiteral(const T (&array)[N]) + { + union { T type_must_be_POD; } dummy; Q_UNUSED(dummy) + + QArrayDataPointerRef result = { QTypedArrayData::allocate(N) }; + Q_CHECK_PTR(result.ptr); + + ::memcpy(result.ptr->data(), array, N * sizeof(T)); + result.ptr->size = N; + + return result; + } +} + +#define Q_ARRAY_LITERAL(Type, Array) \ + QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)( Array ) +#endif // !defined(Q_ARRAY_LITERAL) + QT_END_NAMESPACE QT_END_HEADER diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index d53b90d8a4..f210411643 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -77,6 +77,11 @@ public: d->copyAppend(begin, end); } + SimpleVector(QArrayDataPointerRef ptr) + : d(ptr) + { + } + explicit SimpleVector(Data *ptr) : d(ptr) { diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index d1da18d4e1..55aa2e5e75 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -82,6 +82,8 @@ private slots: void setSharable_data(); void setSharable(); void fromRawData(); + void literals(); + void variadicLiterals(); }; template const T &const_(const T &t) { return t; } @@ -1182,5 +1184,95 @@ void tst_QArrayData::fromRawData() } } +void tst_QArrayData::literals() +{ + { + QArrayDataPointer 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 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 v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(11)); + // v.capacity() is unspecified, for now + +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + QVERIFY(v.isStatic()); +#endif + + QVERIFY(v.isSharable()); + QVERIFY(v.constBegin() + v.size() == v.constEnd()); + + for (int i = 0; i < 10; ++i) + QCOMPARE(const_(v)[i], char('A' + i)); + QCOMPARE(const_(v)[10], char('\0')); + } +} + +void tst_QArrayData::variadicLiterals() +{ +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + { + QArrayDataPointer 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 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 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 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()); + + QVERIFY(v.isSharable()); + QVERIFY(v.constBegin() + v.size() == v.constEnd()); + + for (int i = 0; i < 7; ++i) + QCOMPARE(const_(v)[i], i); + } +#else + QSKIP("Variadic Q_ARRAY_LITERAL not available in current configuration."); +#endif // defined(Q_COMPILER_VARIADIC_MACROS) +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From e32d417b5129171be09a32bb3a65dca6f7464d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 24 Jan 2012 18:05:07 +0100 Subject: [PATCH 34/74] Fix some warnings with Clang The extra bool arguments weren't needed in the first place as they specify the default, but were left behind when allocate parameters were changed from bools to AllocationOptions. Clang saves the day by pointing out the weird conversion going through void ** (!?) Change-Id: Ia0dafce06bf0ee62bd825a2db819c890343b6342 Reviewed-by: Bradley T. Hughes --- tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 55aa2e5e75..46985c1e3b 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -737,7 +737,7 @@ void tst_QArrayData::typedData() { Deallocator keeper(sizeof(char), Q_ALIGNOF(QTypedArrayData::AlignmentDummy)); - QArrayData *array = QTypedArrayData::allocate(10, false); + QArrayData *array = QTypedArrayData::allocate(10); keeper.headers.append(array); QVERIFY(array); @@ -757,7 +757,7 @@ void tst_QArrayData::typedData() { Deallocator keeper(sizeof(short), Q_ALIGNOF(QTypedArrayData::AlignmentDummy)); - QArrayData *array = QTypedArrayData::allocate(10, false); + QArrayData *array = QTypedArrayData::allocate(10); keeper.headers.append(array); QVERIFY(array); @@ -777,7 +777,7 @@ void tst_QArrayData::typedData() { Deallocator keeper(sizeof(double), Q_ALIGNOF(QTypedArrayData::AlignmentDummy)); - QArrayData *array = QTypedArrayData::allocate(10, false); + QArrayData *array = QTypedArrayData::allocate(10); keeper.headers.append(array); QVERIFY(array); From 632840cb0f5ad355d87fc040b015d04af86371ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 1 Feb 2012 11:51:08 +0100 Subject: [PATCH 35/74] Make clear() use the shared null inline There isn't a good reason to impose the additional library call and using the shared null here matches existing behavior in QByteArray, QString and QVector. Change-Id: Idd0bb9c7411db52630402534a11d87cbf2b1e7ba Reviewed-by: Robin Burchell --- src/corelib/tools/qarraydatapointer.h | 2 +- tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 8b5752bc70..215549a2ee 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -141,7 +141,7 @@ public: void clear() { QArrayDataPointer tmp(d); - d = Data::allocate(0); + d = Data::sharedNull(); } bool detach() diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 46985c1e3b..ff0d8bd6de 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -242,7 +242,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v1.isNull()); QVERIFY(v2.isNull()); QVERIFY(v3.isNull()); - QVERIFY(!v4.isNull()); + QVERIFY(v4.isNull()); QVERIFY(!v5.isNull()); QVERIFY(!v6.isNull()); QVERIFY(!v7.isNull()); @@ -306,7 +306,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v1.isSharedWith(v2)); QVERIFY(v1.isSharedWith(v3)); - QVERIFY(!v1.isSharedWith(v4)); + QVERIFY(v1.isSharedWith(v4)); QVERIFY(!v1.isSharedWith(v5)); QVERIFY(!v1.isSharedWith(v6)); From b5148f59917b81dddb73e19bc7a3769a3aa94943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Sun, 5 Feb 2012 14:38:36 +0100 Subject: [PATCH 36/74] QConstStringData -> QStaticStringData The class was renamed in fed603fde515339ec520accefded211ac6f69982. Change-Id: I859f318e80a739f31c2666d0e3088c62b55c5f13 Reviewed-by: Robin Burchell Reviewed-by: Thiago Macieira --- src/corelib/json/qjsonvalue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/json/qjsonvalue.cpp b/src/corelib/json/qjsonvalue.cpp index 603cba8897..ec63db79f5 100644 --- a/src/corelib/json/qjsonvalue.cpp +++ b/src/corelib/json/qjsonvalue.cpp @@ -400,7 +400,7 @@ QString QJsonValue::toString() const if (t != String) return QString(); stringData->ref.ref(); // the constructor below doesn't add a ref. - return QString(*(const QConstStringData<1> *)stringData); + return QString(*(const QStaticStringData<1> *)stringData); } /*! From 475280d5fd13a996ade7a69776fe4d0385114a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Mon, 6 Feb 2012 11:18:18 +0100 Subject: [PATCH 37/74] Update license headers - Updated copyright year, per 1fdfc2abfe1fa26b86028934d4853432e25b4655 - Updated contact information, 629d6eda5cf67122776981de9073857bbc3dcba2 - Drop "All rights reserved", 5635823e17db3395d9b0fa8cfcc72f82fea583f4 (Empty line added to maintain license header line count) Change-Id: Ie401e2b6e40a4b79f4191377dd50dc60be801e1f Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.cpp | 6 +++--- src/corelib/tools/qarraydata.h | 6 +++--- src/corelib/tools/qarraydataops.h | 6 +++--- src/corelib/tools/qarraydatapointer.h | 6 +++--- tests/auto/corelib/tools/qarraydata/simplevector.h | 6 +++--- tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index a693f04042..5ed3ce015f 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index d5d96efc08..1734784c9d 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index 8800cff028..4213e82e84 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 215549a2ee..9316eb0499 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index f210411643..b16025b34a 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index ff0d8bd6de..6a42f4da59 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ From a78bcea5868e8986c5e024a8ca9e01314c11f4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Mon, 6 Feb 2012 11:38:26 +0100 Subject: [PATCH 38/74] Remove use of QT_MODULE from QArrayData stack The macro was made empty in 4ecf82795de54fba530ac9c386f3afff2174edbd, and is no longer necessary or used. Change-Id: If000ff51729e41bdcd1b0409961cf94d50e5f172 Reviewed-by: Thiago Macieira --- src/corelib/tools/qarraydata.h | 2 -- src/corelib/tools/qarraydataops.h | 2 -- src/corelib/tools/qarraydatapointer.h | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 1734784c9d..da2058dccf 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -49,8 +49,6 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -QT_MODULE(Core) - struct Q_CORE_EXPORT QArrayData { QtPrivate::RefCount ref; diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index 4213e82e84..cfb1863d7a 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -51,8 +51,6 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -QT_MODULE(Core) - namespace QtPrivate { template diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 9316eb0499..695a6f8b39 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -48,8 +48,6 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -QT_MODULE(Core) - template struct QArrayDataPointer { From a2abc11b51864ea0bab4fdb3aa44f2ec7cf0cc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 1 Feb 2012 17:57:24 +0100 Subject: [PATCH 39/74] Make QStringBuilder use memcpy for QByteArrayLiteral There is no need to do a bytewise copy looking for the terminating nul-character when the size of the data is statically declared and known ahead of time. Change-Id: I787745a58955d1a366624f9ea92e9e701de8c981 Reviewed-by: Thiago Macieira --- src/corelib/tools/qstringbuilder.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index 1afdde852c..e524523c36 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -376,9 +376,8 @@ template struct QConcatenable > : private QAb #endif static inline void appendTo(const type &ba, char *&out) { - const char *a = ba.ptr->data; - while (*a) - *out++ = *a++; + ::memcpy(out, ba.ptr->data, N); + out += N; } }; From 113e9216844768152ebfe503e413db24fe53a1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 10 Feb 2012 12:36:00 +0100 Subject: [PATCH 40/74] Don't expect null d-pointer in destructors The feature was introduced in commit 83497587b2 (2004, private history), to allow static containers to remain uninitialized until needed. This finishes reverting said commit. The feature had been silently removed from QByteArray and QString in commit a5a0985476 (2004, private history); removed from QList in aef03d80f7. Change-Id: I9947be7758d5730d2d6e6eb2a8a308db6e9bef39 Reviewed-by: Robin Burchell Reviewed-by: Olivier Goffart --- src/corelib/tools/qlinkedlist.h | 2 -- src/corelib/tools/qmap.h | 2 +- src/corelib/tools/qvector.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 1fa4eaabd0..28f190c7fa 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -241,8 +241,6 @@ private: template inline QLinkedList::~QLinkedList() { - if (!d) - return; if (!d->ref.deref()) free(d); } diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index a8306194d4..dc358a8106 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -179,7 +179,7 @@ public: inline QMap() : d(const_cast(&QMapData::shared_null)) { } inline QMap(const QMap &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); } - inline ~QMap() { if (!d) return; if (!d->ref.deref()) freeData(d); } + inline ~QMap() { if (!d->ref.deref()) freeData(d); } QMap &operator=(const QMap &other); #ifdef Q_COMPILER_RVALUE_REFS diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 22ce2a3d8a..6357c24621 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -126,7 +126,7 @@ public: } } - inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } + inline ~QVector() { if (!d->ref.deref()) free(p); } QVector &operator=(const QVector &v); #ifdef Q_COMPILER_RVALUE_REFS inline QVector operator=(QVector &&other) From f9872d12a6fb81aa0efa995720c7d733bbbdd71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 10 Feb 2012 16:13:56 +0100 Subject: [PATCH 41/74] Add support for rvalue-references in QArrayDataPointer I love how this magically makes SimpleVector move-aware :-) Change-Id: I5cb75954d70cf256863c33e684ebc4551ac94f87 Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydatapointer.h | 14 ++++++ .../tools/qarraydata/tst_qarraydata.cpp | 46 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 695a6f8b39..f5ad53aa54 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -86,6 +86,20 @@ public: return *this; } +#ifdef Q_COMPILER_RVALUE_REFS + QArrayDataPointer(QArrayDataPointer &&other) + : d(other.d) + { + other.d = Data::sharedNull(); + } + + QArrayDataPointer &operator=(QArrayDataPointer &&other) + { + this->swap(other); + return *this; + } +#endif + DataOps &operator*() const { Q_ASSERT(d); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 6a42f4da59..00873a84d1 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -84,6 +84,7 @@ private slots: void fromRawData(); void literals(); void variadicLiterals(); + void rValueReferences(); }; template const T &const_(const T &t) { return t; } @@ -1274,5 +1275,50 @@ void tst_QArrayData::variadicLiterals() #endif // defined(Q_COMPILER_VARIADIC_MACROS) } +#ifdef Q_COMPILER_RVALUE_REFS +// std::remove_reference is in C++11, but requires library support +template struct RemoveReference { typedef T Type; }; +template struct RemoveReference { typedef T Type; }; + +// single-argument std::move is in C++11, but requires library support +template +typename RemoveReference::Type &&cxx11Move(T &&t) +{ + return static_cast::Type &&>(t); +} +#endif + +void tst_QArrayData::rValueReferences() +{ +#ifdef Q_COMPILER_RVALUE_REFS + SimpleVector v1(1, 42); + SimpleVector v2; + + const SimpleVector::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 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); +#else + QSKIP("RValue references are not supported in current configuration"); +#endif +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From 75286739de854d761bbfcababa50d1bc6dfda12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 14 Feb 2012 00:02:08 +0100 Subject: [PATCH 42/74] Fix and simplify QString::mid 'position' was being used to initialize 'n' before being fully validated. To compensate for this, the code attempted to fix 'n' once 'position' was found to be invalid (negative). This would, however, also attempt to "fix" a perfectly valid value of 'n'. By fully validating 'position' beforehand we avoid this trap and can safely use it to validate and reset 'n', as needed. Arithmetic for boundary conditions of 'n' was rearranged to avoid triggering integer overflow. Removed explicit check for shared_null, as the same behaviour can be supported without it. Change-Id: Ie9bff7b8137a74f55c7dcfe629bd569045e28f3c Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qstring.cpp | 20 ++---- .../corelib/tools/qstring/tst_qstring.cpp | 70 +++++++++++++++++++ 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 28e8b6be83..e12cf1f910 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -3391,15 +3391,11 @@ QString QString::right(int n) const QString QString::mid(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QString(); - if (n < 0) - n = d->size - position; - if (position < 0) { - n += position; + if (position < 0) position = 0; - } - if (n + position > d->size) + if (n < 0 || n > d->size - position) n = d->size - position; if (position == 0 && n == d->size) return *this; @@ -8061,15 +8057,11 @@ QStringRef QString::rightRef(int n) const QStringRef QString::midRef(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QStringRef(); - if (n < 0) - n = d->size - position; - if (position < 0) { - n += position; + if (position < 0) position = 0; - } - if (n + position > d->size) + if (n < 0 || n > d->size - position) n = d->size - position; return QStringRef(this, position, n); } diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 37d80afd34..7dd801b50b 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -1429,16 +1429,51 @@ void tst_QString::mid() QVERIFY(a.mid(9999).isNull()); QVERIFY(a.mid(9999,1).isNull()); + QCOMPARE(a.mid(-1, 6), QString("ABCDEF")); + QCOMPARE(a.mid(-100, 6), QString("ABCDEF")); + QVERIFY(a.mid(INT_MAX).isNull()); + QVERIFY(a.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.mid(-5, INT_MAX), a); + QCOMPARE(a.mid(-1, INT_MAX), a); + QCOMPARE(a.mid(0, INT_MAX), a); + QCOMPARE(a.mid(1, INT_MAX), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.mid(5, INT_MAX), QString("FGHIEfGEFG")); + QVERIFY(a.mid(20, INT_MAX).isNull()); + QCOMPARE(a.mid(-1, -1), a); + QString n; QVERIFY(n.mid(3,3).isNull()); QVERIFY(n.mid(0,0).isNull()); QVERIFY(n.mid(9999,0).isNull()); QVERIFY(n.mid(9999,1).isNull()); + QVERIFY(n.mid(-1, 6).isNull()); + QVERIFY(n.mid(-100, 6).isNull()); + QVERIFY(n.mid(INT_MAX).isNull()); + QVERIFY(n.mid(INT_MAX, INT_MAX).isNull()); + QVERIFY(n.mid(-5, INT_MAX).isNull()); + QVERIFY(n.mid(-1, INT_MAX).isNull()); + QVERIFY(n.mid(0, INT_MAX).isNull()); + QVERIFY(n.mid(1, INT_MAX).isNull()); + QVERIFY(n.mid(5, INT_MAX).isNull()); + QVERIFY(n.mid(20, INT_MAX).isNull()); + QVERIFY(n.mid(-1, -1).isNull()); + QString x = "Nine pineapples"; QCOMPARE(x.mid(5, 4), QString("pine")); QCOMPARE(x.mid(5), QString("pineapples")); + QCOMPARE(x.mid(-1, 6), QString("Nine p")); + QCOMPARE(x.mid(-100, 6), QString("Nine p")); + QVERIFY(x.mid(INT_MAX).isNull()); + QVERIFY(x.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.mid(-5, INT_MAX), x); + QCOMPARE(x.mid(-1, INT_MAX), x); + QCOMPARE(x.mid(0, INT_MAX), x); + QCOMPARE(x.mid(1, INT_MAX), QString("ine pineapples")); + QCOMPARE(x.mid(5, INT_MAX), QString("pineapples")); + QVERIFY(x.mid(20, INT_MAX).isNull()); + QCOMPARE(x.mid(-1, -1), x); } void tst_QString::midRef() @@ -1455,16 +1490,51 @@ void tst_QString::midRef() QVERIFY(a.midRef(9999).toString().isEmpty()); QVERIFY(a.midRef(9999,1).toString().isEmpty()); + QCOMPARE(a.midRef(-1, 6).toString(), QString("ABCDEF")); + QCOMPARE(a.midRef(-100, 6).toString(), QString("ABCDEF")); + QVERIFY(a.midRef(INT_MAX).isNull()); + QVERIFY(a.midRef(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.midRef(-5, INT_MAX).toString(), a); + QCOMPARE(a.midRef(-1, INT_MAX).toString(), a); + QCOMPARE(a.midRef(0, INT_MAX).toString(), a); + QCOMPARE(a.midRef(1, INT_MAX).toString(), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.midRef(5, INT_MAX).toString(), QString("FGHIEfGEFG")); + QVERIFY(a.midRef(20, INT_MAX).isNull()); + QCOMPARE(a.midRef(-1, -1).toString(), a); + QString n; QVERIFY(n.midRef(3,3).toString().isEmpty()); QVERIFY(n.midRef(0,0).toString().isEmpty()); QVERIFY(n.midRef(9999,0).toString().isEmpty()); QVERIFY(n.midRef(9999,1).toString().isEmpty()); + QVERIFY(n.midRef(-1, 6).isNull()); + QVERIFY(n.midRef(-100, 6).isNull()); + QVERIFY(n.midRef(INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MAX, INT_MAX).isNull()); + QVERIFY(n.midRef(-5, INT_MAX).isNull()); + QVERIFY(n.midRef(-1, INT_MAX).isNull()); + QVERIFY(n.midRef(0, INT_MAX).isNull()); + QVERIFY(n.midRef(1, INT_MAX).isNull()); + QVERIFY(n.midRef(5, INT_MAX).isNull()); + QVERIFY(n.midRef(20, INT_MAX).isNull()); + QVERIFY(n.midRef(-1, -1).isNull()); + QString x = "Nine pineapples"; QCOMPARE(x.midRef(5, 4).toString(), QString("pine")); QCOMPARE(x.midRef(5).toString(), QString("pineapples")); + QCOMPARE(x.midRef(-1, 6).toString(), QString("Nine p")); + QCOMPARE(x.midRef(-100, 6).toString(), QString("Nine p")); + QVERIFY(x.midRef(INT_MAX).isNull()); + QVERIFY(x.midRef(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.midRef(-5, INT_MAX).toString(), x); + QCOMPARE(x.midRef(-1, INT_MAX).toString(), x); + QCOMPARE(x.midRef(0, INT_MAX).toString(), x); + QCOMPARE(x.midRef(1, INT_MAX).toString(), QString("ine pineapples")); + QCOMPARE(x.midRef(5, INT_MAX).toString(), QString("pineapples")); + QVERIFY(x.midRef(20, INT_MAX).isNull()); + QCOMPARE(x.midRef(-1, -1).toString(), x); } void tst_QString::stringRef() From 5d593da3d31a0c6adffb449db3ceb65328b87cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 31 Jan 2012 18:31:58 +0100 Subject: [PATCH 43/74] Remove constructors taking implicit string sizes Constructors taking explicit sizes got a default -1 size argument that triggers length calculation from nul-terminated strings. This imposes a slight change in behavior: negative size arguments would previously be ignored and generate an empty string whereas with this patch we expect to see a nul-terminated string. On the other hand, keeping the previous behavior could effectively hide errors in user code and I can't find a good reason to support it. Documentation for the constructors was updated and made more consistent between the classes. Change-Id: I738ac3298cffe3221c8a56e85ba2102623e7b67d Reviewed-by: Lars Knoll --- dist/changes-5.0.0 | 7 ++++ src/corelib/tools/qbytearray.cpp | 56 +++++++++---------------- src/corelib/tools/qbytearray.h | 3 +- src/corelib/tools/qstring.cpp | 71 ++++++++++++-------------------- src/corelib/tools/qstring.h | 3 +- 5 files changed, 54 insertions(+), 86 deletions(-) diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index fc5bebd11d..e73ecc50ee 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -36,6 +36,13 @@ information about a particular change. - QCoreApplication::translate() will no longer return the source text when the translation is empty. Use lrelease -removeidentical for optimization. +- QString and QByteArray constructors that take a size argument will now treat + negative sizes to indicate nul-terminated strings (a nul-terminated array of + QChar, in the case of QString). In Qt 4, negative sizes were ignored and + result in empty QString and QByteArray, respectively. The size argument to + those constructors now has a default value of -1, thus replacing the separate + constructors that did the same. + - Qt::escape() is deprecated (but can be enabled via QT_DISABLE_DEPRECATED_BEFORE), use QString::toHtmlEscaped() instead. diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index aa22413d94..90069b112d 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -1287,38 +1287,16 @@ void QByteArray::chop(int n) \sa isEmpty() */ -/*! \fn QByteArray::QByteArray(const char *str) - - Constructs a byte array initialized with the string \a str. - - QByteArray makes a deep copy of the string data. -*/ - -QByteArray::QByteArray(const char *str) -{ - if (!str) { - d = const_cast(&shared_null.ba); - } else if (!*str) { - d = const_cast(&shared_empty.ba); - } else { - int len = qstrlen(str); - d = static_cast(malloc(sizeof(Data) + len + 1)); - Q_CHECK_PTR(d); - d->ref.initializeOwned(); - d->size = len; - d->alloc = len; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), str, len+1); // include null terminator - } -} - /*! Constructs a byte array containing the first \a size bytes of array \a data. If \a data is 0, a null byte array is constructed. + If \a size is negative, \a data is assumed to point to a nul-terminated + string and its length is determined dynamically. The terminating + nul-character is not considered part of the byte array. + QByteArray makes a deep copy of the string data. \sa fromRawData() @@ -1328,18 +1306,22 @@ QByteArray::QByteArray(const char *data, int size) { if (!data) { d = const_cast(&shared_null.ba); - } else if (size <= 0) { - d = const_cast(&shared_empty.ba); } else { - d = static_cast(malloc(sizeof(Data) + size + 1)); - Q_CHECK_PTR(d); - d->ref.initializeOwned(); - d->size = size; - d->alloc = size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), data, size); - d->data()[size] = '\0'; + if (size < 0) + size = strlen(data); + if (!size) { + d = const_cast(&shared_empty.ba); + } else { + d = static_cast(malloc(sizeof(Data) + size + 1)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = 0; + memcpy(d->data(), data, size); + d->data()[size] = '\0'; + } } } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 5efeb6025e..c4feb63814 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -180,8 +180,7 @@ private: public: inline QByteArray(); - QByteArray(const char *); - QByteArray(const char *, int size); + QByteArray(const char *, int size = -1); QByteArray(int size, char c); QByteArray(int size, Qt::Initialization); inline QByteArray(const QByteArray &); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index e12cf1f910..0f7d398972 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1022,62 +1022,43 @@ int QString::toUcs4_helper(const ushort *uc, int length, uint *out) Constructs a string initialized with the first \a size characters of the QChar array \a unicode. + If \a unicode is 0, a null string is constructed. + + If \a size is negative, \a unicode is assumed to point to a nul-terminated + array and its length is determined dynamically. The terminating + nul-character is not considered part of the string. + QString makes a deep copy of the string data. The unicode data is copied as is and the Byte Order Mark is preserved if present. + + \sa fromRawData() */ QString::QString(const QChar *unicode, int size) { if (!unicode) { d = const_cast(&shared_null.str); - } else if (size <= 0) { - d = const_cast(&shared_empty.str); } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref.initializeOwned(); - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; + if (size < 0) { + size = 0; + while (unicode[size] != 0) + ++size; + } + if (!size) { + d = const_cast(&shared_empty.str); + } else { + d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = 0; + memcpy(d->data(), unicode, size * sizeof(QChar)); + d->data()[size] = '\0'; + } } } -/*! - \since 4.7 - - Constructs a string initialized with the characters of the QChar array - \a unicode, which must be terminated with a 0. - - QString makes a deep copy of the string data. The unicode data is copied as - is and the Byte Order Mark is preserved if present. -*/ -QString::QString(const QChar *unicode) -{ - if (!unicode) { - d = const_cast(&shared_null.str); - } else { - int size = 0; - while (unicode[size] != 0) - ++size; - if (!size) { - d = const_cast(&shared_empty.str); - } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref.initializeOwned(); - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; - } - } -} - - /*! Constructs a string of the given \a size with every character set to \a ch. diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index a857d2d4e5..df0ce73b3a 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -158,8 +158,7 @@ public: typedef QStringData Data; inline QString(); - QString(const QChar *unicode, int size); // Qt5: don't cap size < 0 - explicit QString(const QChar *unicode); // Qt5: merge with the above + explicit QString(const QChar *unicode, int size = -1); QString(QChar c); QString(int size, QChar c); inline QString(const QLatin1String &latin1); From fb8be9905d5f3216edc3fbb72b8ce1c380737eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 16 Feb 2012 19:59:07 +0100 Subject: [PATCH 44/74] qAllocMore: Always grow exponentially qAllocMore is used by growing containers to allocate additional memory for future growth. The previous algorithm would grow linearly in increments of 8 up to 64 and then progress exponentially in powers of two. The new (constant time) algorithm does away with the linear segment and always progresses exponentially. It also has the nice benefit of cleanly avoiding undefined behaviour that the old implementation tried hard to circumvent. Besides always progressing exponentially, the next-power-of-two algorithm was tweaked to always include space for growth. Previously queries at boundary values (powers of two) would return the same value. The test was updated to verify sanity of results. As the algorithm is well behaved, testing of bogus data was dropped. Whatever happens in those cases is irrelevant, anyway: the bug lives elsewhere. Change-Id: I4def473cce4b438734887084e3c3bd8da0ff466b Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/tools/qbytearray.cpp | 37 ++++++++++--------- .../tools/qbytearray/tst_qbytearray.cpp | 29 +++++++++------ 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 90069b112d..559ba193d1 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -69,24 +69,25 @@ int qFindByteArray( int qAllocMore(int alloc, int extra) { - if (alloc == 0 && extra == 0) - return 0; - const int page = 1 << 12; - int nalloc; - alloc += extra; - if (alloc < 1<<6) { - nalloc = (1<<3) + ((alloc >>3) << 3); - } else { - // don't do anything if the loop will overflow signed int. - if (alloc >= INT_MAX/2) - return INT_MAX; - nalloc = (alloc < page) ? 1 << 3 : page; - while (nalloc < alloc) { - if (nalloc <= 0) - return INT_MAX; - nalloc *= 2; - } - } + Q_ASSERT(alloc >= 0 && extra >= 0); + Q_ASSERT(alloc < (1 << 30) - extra); + + unsigned nalloc = alloc + extra; + + // Round up to next power of 2 + + // Assuming container is growing, always overshoot + //--nalloc; + + nalloc |= nalloc >> 1; + nalloc |= nalloc >> 2; + nalloc |= nalloc >> 4; + nalloc |= nalloc >> 8; + nalloc |= nalloc >> 16; + ++nalloc; + + Q_ASSERT(nalloc > unsigned(alloc + extra)); + return nalloc - extra; } diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp index 585d6afa44..4fb74a2af5 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -1075,18 +1075,25 @@ void tst_QByteArray::toULongLong() // global function defined in qbytearray.cpp void tst_QByteArray::qAllocMore() { - static const int t[] = { - INT_MIN, INT_MIN + 1, -1234567, -66000, -1025, - -3, -1, 0, +1, +3, +1025, +66000, +1234567, INT_MAX - 1, INT_MAX, - INT_MAX/3 - }; - static const int N = sizeof(t)/sizeof(t[0]); + using QT_PREPEND_NAMESPACE(qAllocMore); - // make sure qAllocMore() doesn't loop infinitely on any input - for (int i = 0; i < N; ++i) { - for (int j = 0; j < N; ++j) { - ::qAllocMore(t[i], t[j]); - } + // Not very important, but please behave :-) + QVERIFY(qAllocMore(0, 0) >= 0); + + for (int i = 1; i < 1 << 8; i <<= 1) + QVERIFY(qAllocMore(i, 0) >= i); + + for (int i = 1 << 8; i < 1 << 30; i <<= 1) { + const int alloc = qAllocMore(i, 0); + + QVERIFY(alloc >= i); + QCOMPARE(qAllocMore(i - 8, 8), alloc - 8); + QCOMPARE(qAllocMore(i - 16, 16), alloc - 16); + QCOMPARE(qAllocMore(i - 24, 24), alloc - 24); + QCOMPARE(qAllocMore(i - 32, 32), alloc - 32); + + QVERIFY(qAllocMore(i - 1, 0) >= i - 1); + QVERIFY(qAllocMore(i + 1, 0) >= i + 1); } } From fd96115ae8501c2baf591b821bdf6f7d90b62f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 16 Feb 2012 23:53:58 +0100 Subject: [PATCH 45/74] QVector: always grow exponentially For non-movable types (QTypeInfo::isStatic), QVector would grow the array linearly, and defer to qAllocMore otherwise. That property, however, gives no indication as to how the vector will grow. By forcing additional allocations for growing containers of such types, this penalized exactly those objects which are more expensive to move. We now let qAllocMore reign in growth decisions. Change-Id: I843a89dcdc21d09868c6b62a846a7e1e4548e399 Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/tools/qvector.cpp | 4 +--- src/corelib/tools/qvector.h | 9 ++++----- tests/benchmarks/corelib/tools/qvector/qrawvector.h | 8 +++----- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 0a15c2208c..254dd34771 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -76,10 +76,8 @@ void QVectorData::free(QVectorData *x, int alignment) ::free(x); } -int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive) +int QVectorData::grow(int sizeofTypedData, int size, int sizeofT) { - if (excessive) - return size + size / 2; return qAllocMore(size * sizeofT, sizeofTypedData - sizeofT) / sizeofT; } diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 6357c24621..4f9e1839ec 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -80,7 +80,7 @@ struct Q_CORE_EXPORT QVectorData static QVectorData *allocate(int size, int alignment); static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); static void free(QVectorData *data, int alignment); - static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive); + static int grow(int sizeofTypedData, int size, int sizeofT); }; template @@ -355,7 +355,7 @@ void QVector::reserve(int asize) template void QVector::resize(int asize) { realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? - QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo::isStatic) + QVectorData::grow(sizeOfTypedData(), asize, sizeof(T)) : d->alloc); } template inline void QVector::clear() @@ -582,7 +582,7 @@ void QVector::append(const T &t) if (!isDetached() || d->size + 1 > d->alloc) { const T copy(t); realloc(d->size, (d->size + 1 > d->alloc) ? - QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo::isStatic) + QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T)) : d->alloc); if (QTypeInfo::isComplex) new (p->array + d->size) T(copy); @@ -604,8 +604,7 @@ typename QVector::iterator QVector::insert(iterator before, size_type n, c if (n != 0) { const T copy(t); if (!isDetached() || d->size + n > d->alloc) - realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), - QTypeInfo::isStatic)); + realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo::isStatic) { T *b = p->array + d->size; T *i = p->array + d->size + n; diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index 159dfbf8dc..4a4f03ee1b 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -308,7 +308,7 @@ void QRawVector::reserve(int asize) template void QRawVector::resize(int asize) { realloc(asize, (asize > m_alloc || (asize < m_size && asize < (m_alloc >> 1))) - ? QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo::isStatic) + ? QVectorData::grow(sizeOfTypedData(), asize, sizeof(T)) : m_alloc, false); } template inline void QRawVector::clear() @@ -510,8 +510,7 @@ void QRawVector::append(const T &t) { if (m_size + 1 > m_alloc) { const T copy(t); - realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + 1, sizeof(T), - QTypeInfo::isStatic), false); + realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + 1, sizeof(T)), false); if (QTypeInfo::isComplex) new (m_begin + m_size) T(copy); else @@ -532,8 +531,7 @@ typename QRawVector::iterator QRawVector::insert(iterator before, size_typ if (n != 0) { const T copy(t); if (m_size + n > m_alloc) - realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + n, sizeof(T), - QTypeInfo::isStatic), false); + realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + n, sizeof(T)), false); if (QTypeInfo::isStatic) { T *b = m_begin + m_size; T *i = m_begin + m_size + n; From ec5eb45cd3e6d60744afd9e4b16e36fbba3eaf05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 16 Feb 2012 23:11:26 +0100 Subject: [PATCH 46/74] Add header to .pri Commit 7d16ea40 introduced QArrayDataPointer and respective header file, but missed making it known to qmake. Change-Id: I48c1831730cc4d27c6600c090a719861bb71a301 Reviewed-by: Thiago Macieira --- src/corelib/tools/tools.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 21407fa9cd..0342e53261 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -4,6 +4,7 @@ HEADERS += \ tools/qalgorithms.h \ tools/qarraydata.h \ tools/qarraydataops.h \ + tools/qarraydatapointer.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ From ebeebe212624b0d6fb87266629dce20ee75391bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 17 Feb 2012 11:23:41 +0100 Subject: [PATCH 47/74] Have QVectorData::grow, take size of header w/ padding This includes padding necessary to align the data array, but excludes the first element as was done before. Size of header is the interesting piece of information, anyway. This simplifies calculations in a couple of places, harmonizes code with the QRawVector fork and paves the way for further changes in QVector, namely the memory layout. When Q_ALIGNOF is not available, default to pointer-size alignment. This should be honoured by malloc and won't trigger use of more expensive aligned allocation. Change-Id: I504022ac7595f69089cafd96e47a91b874d5771e Reviewed-by: Thiago Macieira --- src/corelib/tools/qvector.cpp | 4 +- src/corelib/tools/qvector.h | 31 +++++++------ .../corelib/tools/qvector/qrawvector.h | 46 ++++++++----------- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 254dd34771..b70436f907 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -76,9 +76,9 @@ void QVectorData::free(QVectorData *x, int alignment) ::free(x); } -int QVectorData::grow(int sizeofTypedData, int size, int sizeofT) +int QVectorData::grow(int sizeOfHeader, int size, int sizeOfT) { - return qAllocMore(size * sizeofT, sizeofTypedData - sizeofT) / sizeofT; + return qAllocMore(size * sizeOfT, sizeOfHeader) / sizeOfT; } /*! diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 4f9e1839ec..b47dddab14 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -80,7 +80,7 @@ struct Q_CORE_EXPORT QVectorData static QVectorData *allocate(int size, int alignment); static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); static void free(QVectorData *data, int alignment); - static int grow(int sizeofTypedData, int size, int sizeofT); + static int grow(int sizeOfHeader, int size, int sizeOfT); }; template @@ -331,17 +331,18 @@ private: QVectorData *malloc(int alloc); void realloc(int size, int alloc); void free(Data *d); - int sizeOfTypedData() { - // this is more or less the same as sizeof(Data), except that it doesn't - // count the padding at the end - return reinterpret_cast(&(reinterpret_cast(this))->array[1]) - reinterpret_cast(this); + + static Q_DECL_CONSTEXPR int offsetOfTypedData() + { + // (non-POD)-safe offsetof(Data, array) + return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } - inline int alignOfTypedData() const + static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return qMax(sizeof(void*), Q_ALIGNOF(Data)); + return Q_ALIGNOF(Data); #else - return 0; + return sizeof(void *); #endif } }; @@ -355,7 +356,7 @@ void QVector::reserve(int asize) template void QVector::resize(int asize) { realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? - QVectorData::grow(sizeOfTypedData(), asize, sizeof(T)) + QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) : d->alloc); } template inline void QVector::clear() @@ -413,7 +414,7 @@ QVector &QVector::operator=(const QVector &v) template inline QVectorData *QVector::malloc(int aalloc) { - QVectorData *vectordata = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *vectordata = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(vectordata); return vectordata; } @@ -508,13 +509,13 @@ void QVector::realloc(int asize, int aalloc) if (QTypeInfo::isComplex) { x.d->size = 0; } else { - ::memcpy(x.p, p, sizeOfTypedData() + (qMin(aalloc, d->alloc) - 1) * sizeof(T)); + ::memcpy(x.p, p, offsetOfTypedData() + qMin(aalloc, d->alloc) * sizeof(T)); x.d->size = d->size; } } else { QT_TRY { - QVectorData *mem = QVectorData::reallocate(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T), - sizeOfTypedData() + (d->alloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *mem = QVectorData::reallocate(d, offsetOfTypedData() + aalloc * sizeof(T), + offsetOfTypedData() + d->alloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); x.d = d = mem; x.d->size = d->size; @@ -582,7 +583,7 @@ void QVector::append(const T &t) if (!isDetached() || d->size + 1 > d->alloc) { const T copy(t); realloc(d->size, (d->size + 1 > d->alloc) ? - QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T)) + QVectorData::grow(offsetOfTypedData(), d->size + 1, sizeof(T)) : d->alloc); if (QTypeInfo::isComplex) new (p->array + d->size) T(copy); @@ -604,7 +605,7 @@ typename QVector::iterator QVector::insert(iterator before, size_type n, c if (n != 0) { const T copy(t); if (!isDetached() || d->size + n > d->alloc) - realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T))); + realloc(d->size, QVectorData::grow(offsetOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo::isStatic) { T *b = p->array + d->size; T *i = p->array + d->size + n; diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index 4a4f03ee1b..1342513a7f 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -73,17 +73,11 @@ class QRawVector int m_alloc; public: - //static Data dummy; - //int headerOffset() { return (char*)&dummy.array - (char*)&dummy; } - inline int headerOffset() const { - // gcc complains about: return offsetof(Data, array); and also - // does not like '0' in the expression below. - return (char *)&(((Data *)(1))->array) - (char *)1; - } - inline Data *toBase(T *begin) const - { return (Data*)((char*)begin - headerOffset()); } - inline T *fromBase(void *d) const - { return (T*)((char*)d + headerOffset()); } + static Data *toBase(T *begin) + { return (Data*)((char*)begin - offsetOfTypedData()); } + static T *fromBase(void *d) + { return (T*)((char*)d + offsetOfTypedData()); } + inline QRawVector() { m_begin = fromBase(0); m_alloc = m_size = 0; realloc(m_size, m_alloc, true); } explicit QRawVector(int size); @@ -270,17 +264,18 @@ private: T *allocate(int alloc); void realloc(int size, int alloc, bool ref); void free(T *begin, int size); - int sizeOfTypedData() { - // this is more or less the same as sizeof(Data), except that it doesn't - // count the padding at the end - return reinterpret_cast(&(reinterpret_cast(this))->array[1]) - reinterpret_cast(this); + + static Q_DECL_CONSTEXPR int offsetOfTypedData() + { + // (non-POD)-safe offsetof(Data, array) + return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } - static inline int alignOfTypedData() + static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return qMax(sizeof(void*), Q_ALIGNOF(Data)); + return Q_ALIGNOF(Data); #else - return 0; + return sizeof(void *); #endif } @@ -308,7 +303,7 @@ void QRawVector::reserve(int asize) template void QRawVector::resize(int asize) { realloc(asize, (asize > m_alloc || (asize < m_size && asize < (m_alloc >> 1))) - ? QVectorData::grow(sizeOfTypedData(), asize, sizeof(T)) + ? QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) : m_alloc, false); } template inline void QRawVector::clear() @@ -369,7 +364,7 @@ QRawVector &QRawVector::operator=(const QRawVector &v) template inline T *QRawVector::allocate(int aalloc) { - QVectorData *d = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *d = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(d); return fromBase(d); } @@ -445,10 +440,9 @@ void QRawVector::realloc(int asize, int aalloc, bool ref) changed = true; } else { QT_TRY { - QVectorData *mem = QVectorData::reallocate( - toBase(m_begin), sizeOfTypedData() + (aalloc - 1) * sizeof(T), - sizeOfTypedData() -+ (xalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *mem = QVectorData::reallocate(toBase(m_begin), + offsetOfTypedData() + aalloc * sizeof(T), + offsetOfTypedData() + xalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); xbegin = fromBase(mem); xsize = m_size; @@ -510,7 +504,7 @@ void QRawVector::append(const T &t) { if (m_size + 1 > m_alloc) { const T copy(t); - realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + 1, sizeof(T)), false); + realloc(m_size, QVectorData::grow(offsetOfTypedData(), m_size + 1, sizeof(T)), false); if (QTypeInfo::isComplex) new (m_begin + m_size) T(copy); else @@ -531,7 +525,7 @@ typename QRawVector::iterator QRawVector::insert(iterator before, size_typ if (n != 0) { const T copy(t); if (m_size + n > m_alloc) - realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + n, sizeof(T)), false); + realloc(m_size, QVectorData::grow(offsetOfTypedData(), m_size + n, sizeof(T)), false); if (QTypeInfo::isStatic) { T *b = m_begin + m_size; T *i = m_begin + m_size + n; From 4c8a4058c359c8d163c643120426079fc80c8214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 17 Feb 2012 14:11:28 +0100 Subject: [PATCH 48/74] Change meaning of offset in QByteArrayData It used to be an index into the first element in 'd' that came after 'offset'. It is now the byte offset from the beginning of the QByteArrayData structure. By no longer using an actual array to access characters, we also steer clear of GCC bug #43247: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247 This aligns this data structure with QArrayData. The intention is to have QVector, QString and QByteArray share the same memory layout and possibly code. Change-Id: I8546e5f51cd2161ba09bd4ada174b7f5e6f09db7 Reviewed-by: Thiago Macieira --- src/corelib/tools/qbytearray.cpp | 34 +++++++++---------- src/corelib/tools/qbytearray.h | 17 +++++----- .../tools/qbytearray/tst_qbytearray.cpp | 2 +- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 559ba193d1..0d5c0f59ba 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -57,7 +57,7 @@ #include #include -#define IS_RAW_DATA(d) ((d)->offset != 0) +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QByteArrayData)) QT_BEGIN_NAMESPACE @@ -555,7 +555,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) } d.take(); // realloc was successful d.reset(p); - d->offset = 0; + d->offset = sizeof(QByteArrayData); int res = ::uncompress((uchar*)d->data(), &len, (uchar*)data+4, nbytes-4); @@ -581,7 +581,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) d->size = len; d->alloc = len; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[len] = 0; return QByteArray(d.take(), 0, 0); @@ -616,9 +616,9 @@ static inline char qToLower(char c) } const QStaticByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, - 0, 0, 0, { 0 } }, { 0 } }; + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; const QStaticByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, - 0, 0, 0, { 0 } }, { 0 } }; + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; /*! \class QByteArray @@ -1319,7 +1319,7 @@ QByteArray::QByteArray(const char *data, int size) d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); memcpy(d->data(), data, size); d->data()[size] = '\0'; } @@ -1344,7 +1344,7 @@ QByteArray::QByteArray(int size, char ch) d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); memset(d->data(), ch, size); d->data()[size] = '\0'; } @@ -1364,7 +1364,7 @@ QByteArray::QByteArray(int size, Qt::Initialization) d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[size] = '\0'; } @@ -1386,7 +1386,7 @@ void QByteArray::resize(int size) if (size < 0) size = 0; - if (d->offset && !d->ref.isShared() && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1411,7 +1411,7 @@ void QByteArray::resize(int size) x->size = size; x->alloc = size; x->capacityReserved = false; - x->offset = 0; + x->offset = sizeof(QByteArrayData); x->data()[size] = '\0'; d = x; } else { @@ -1446,14 +1446,14 @@ QByteArray &QByteArray::fill(char ch, int size) void QByteArray::realloc(int alloc) { - if (d->ref.isShared() || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { Data *x = static_cast(malloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = alloc; x->capacityReserved = d->capacityReserved; - x->offset = 0; + x->offset = sizeof(QByteArrayData); ::memcpy(x->data(), d->data(), x->size); x->data()[x->size] = '\0'; if (!d->ref.deref()) @@ -1463,7 +1463,7 @@ void QByteArray::realloc(int alloc) Data *x = static_cast(::realloc(d, sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->alloc = alloc; - x->offset = 0; + x->offset = sizeof(QByteArrayData); d = x; } } @@ -1485,7 +1485,7 @@ void QByteArray::expand(int i) QByteArray QByteArray::nulTerminated() const { // is this fromRawData? - if (!d->offset) + if (!IS_RAW_DATA(d)) return *this; // no, then we're sure we're zero terminated QByteArray copy(*this); @@ -3874,7 +3874,7 @@ QByteArray QByteArray::fromRawData(const char *data, int size) x->size = size; x->alloc = 0; x->capacityReserved = false; - x->offset = data - (x->d + sizeof(qptrdiff)); + x->offset = data - reinterpret_cast(x); } return QByteArray(x, 0, 0); } @@ -3900,9 +3900,9 @@ QByteArray &QByteArray::setRawData(const char *data, uint size) } else { if (data) { d->size = size; - d->offset = data - (d->d + sizeof(qptrdiff)); + d->offset = data - reinterpret_cast(d); } else { - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->size = 0; *d->data() = 0; } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index c4feb63814..c654673959 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -124,12 +124,11 @@ struct QByteArrayData int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - char d[sizeof(qptrdiff)]; - }; - inline char *data() { return d + sizeof(qptrdiff) + offset; } - inline const char *data() const { return d + sizeof(qptrdiff) + offset; } + + qptrdiff offset; + + inline char *data() { return reinterpret_cast(this) + offset; } + inline const char *data() const { return reinterpret_cast(this) + offset; } }; template struct QStaticByteArrayData @@ -148,7 +147,7 @@ template struct QStaticByteArrayDataPtr # define QByteArrayLiteral(str) ([]() -> QStaticByteArrayDataPtr { \ enum { Size = sizeof(str) - 1 }; \ static const QStaticByteArrayData qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QByteArrayData) }, str }; \ QStaticByteArrayDataPtr holder = { &qbytearray_literal }; \ return holder; }()) @@ -161,7 +160,7 @@ template struct QStaticByteArrayDataPtr __extension__ ({ \ enum { Size = sizeof(str) - 1 }; \ static const QStaticByteArrayData qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QByteArrayData) }, str }; \ QStaticByteArrayDataPtr holder = { &qbytearray_literal }; \ holder; }) #endif @@ -427,7 +426,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) realloc(d->size); } +{ if (d->ref.isShared() || (d->offset != sizeof(QByteArrayData))) realloc(d->size); } inline bool QByteArray::isDetached() const { return !d->ref.isShared(); } inline QByteArray::QByteArray(const QByteArray &a) : d(a.d) diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp index 4fb74a2af5..e442aa7e1a 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -1592,7 +1592,7 @@ void tst_QByteArray::literals() QVERIFY(str.length() == 4); QVERIFY(str == "abcd"); QVERIFY(str.data_ptr()->ref.isStatic()); - QVERIFY(str.data_ptr()->offset == 0); + QVERIFY(str.data_ptr()->offset == sizeof(QByteArrayData)); const char *s = str.constData(); QByteArray str2 = str; From 558049e972fde4e4ba3f60c1982b63d324385bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 21 Feb 2012 16:32:35 +0100 Subject: [PATCH 49/74] Fix warnings on shadowing data members with arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example emitted by GCC 4.2.1: warning: declaration of ‘iter’ shadows a member of 'this' Change-Id: I288da01c511a1404bf41881a6c96a5f3cd00d0a7 Reviewed-by: Jędrzej Nowacki --- src/corelib/tools/qarraydataops.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index cfb1863d7a..1b8ed3372d 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -163,9 +163,9 @@ struct QGenericArrayOps struct Destructor { - Destructor(T *&iter) - : iter(&iter) - , end(iter) + Destructor(T *&it) + : iter(&it) + , end(it) { } @@ -231,10 +231,10 @@ struct QMovableArrayOps struct ReversibleDisplace { - ReversibleDisplace(T *begin, T *end, size_t displace) - : begin(begin) - , end(end) - , displace(displace) + ReversibleDisplace(T *start, T *finish, size_t diff) + : begin(start) + , end(finish) + , displace(diff) { ::memmove(begin + displace, begin, (end - begin) * sizeof(T)); } @@ -255,7 +255,7 @@ struct QMovableArrayOps struct CopyConstructor { - CopyConstructor(T *where) : where(where) {} + CopyConstructor(T *w) : where(w) {} void copy(const T *src, const T *const srcEnd) { From 3fe1eed0537c3b08a51295b544e0620ade1eca22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Wed, 22 Feb 2012 01:25:28 +0100 Subject: [PATCH 50/74] Workaround compiler issue I can't figure this one out, but it seems to be a clang compiler bug that is triggered in association with -DQT_NO_DEBUG. Changing the test from QVERIFY to QCOMPARE keeps the intent and the check, but makes the failure go away. It can't hurt... Change-Id: Ib34e5e850e5b731d729e417430dec55e372805ac Reviewed-by: Chris Adams --- tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 00873a84d1..561491da00 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1216,7 +1216,7 @@ void tst_QArrayData::literals() #endif QVERIFY(v.isSharable()); - QVERIFY(v.constBegin() + v.size() == v.constEnd()); + QCOMPARE(v.constBegin() + v.size(), v.constEnd()); for (int i = 0; i < 10; ++i) QCOMPARE(const_(v)[i], char('A' + i)); From a5233b6b22e20bf249d8ae993a4224c6874abccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 17 Feb 2012 14:18:45 +0100 Subject: [PATCH 51/74] Change meaning of offset in QStringData It used to be an index into the first element in 'd' that came after 'offset'. It is now the byte offset from the beginning of the QStringData structure. By no longer using an actual array to access characters, we also steer clear of GCC bug #43247: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247 This aligns this data structure with QArrayData. The intention is to have QVector, QString and QByteArray share the same memory layout and possibly code. Change-Id: I4850813e1bd47c3cb670c50c9a8ccc1bff2e8597 Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 32 ++++++++++--------- src/corelib/tools/qstring.h | 17 +++++----- .../corelib/tools/qstring/tst_qstring.cpp | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 57210901ee..be6f48808c 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -94,6 +94,8 @@ #define ULLONG_MAX quint64_C(18446744073709551615) #endif +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QStringData)) + QT_BEGIN_NAMESPACE #ifndef QT_NO_TEXTCODEC @@ -800,8 +802,8 @@ const QString::Null QString::null = { }; \sa split() */ -const QStaticStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; -const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; +const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; int QString::grow(int size) { @@ -1052,7 +1054,7 @@ QString::QString(const QChar *unicode, int size) d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); memcpy(d->data(), unicode, size * sizeof(QChar)); d->data()[size] = '\0'; } @@ -1076,7 +1078,7 @@ QString::QString(int size, QChar ch) d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *i = d->data() + size; ushort *b = d->data(); @@ -1100,7 +1102,7 @@ QString::QString(int size, Qt::Initialization) d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; } @@ -1122,7 +1124,7 @@ QString::QString(QChar ch) d->size = 1; d->alloc = 1; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[0] = ch.unicode(); d->data()[1] = '\0'; } @@ -1220,7 +1222,7 @@ void QString::resize(int size) if (size < 0) size = 0; - if (d->offset && !d->ref.isShared() && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1294,14 +1296,14 @@ void QString::resize(int size) // ### Qt 5: rename reallocData() to avoid confusion. 197625 void QString::realloc(int alloc) { - if (d->ref.isShared() || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { Data *x = static_cast(::malloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = (uint) alloc; x->capacityReserved = d->capacityReserved; - x->offset =0; + x->offset = sizeof(QStringData); ::memcpy(x->data(), d->data(), x->size * sizeof(QChar)); x->data()[x->size] = 0; if (!d->ref.deref()) @@ -1312,7 +1314,7 @@ void QString::realloc(int alloc) Q_CHECK_PTR(p); d = p; d->alloc = alloc; - d->offset = 0; + d->offset = sizeof(QStringData); } } @@ -3741,7 +3743,7 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *dst = d->data(); /* SIMD: @@ -4769,7 +4771,7 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, const ushort *QString::utf16() const { - if (d->offset) + if (IS_RAW_DATA(d)) const_cast(this)->realloc(); // ensure '\\0'-termination for ::fromRawData strings return d->data(); } @@ -7050,7 +7052,7 @@ QString QString::fromRawData(const QChar *unicode, int size) x->size = size; x->alloc = 0; x->capacityReserved = false; - x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); + x->offset = reinterpret_cast(unicode) - reinterpret_cast(x); } return QString(x, 0); } @@ -7076,9 +7078,9 @@ QString &QString::setRawData(const QChar *unicode, int size) } else { if (unicode) { d->size = size; - d->offset = (const ushort *)unicode - (d->d + sizeof(qptrdiff)/sizeof(ushort)); + d->offset = reinterpret_cast(unicode) - reinterpret_cast(d); } else { - d->offset = 0; + d->offset = sizeof(QStringData); d->size = 0; } } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index df0ce73b3a..90fed78a15 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -75,12 +75,11 @@ struct QStringData { int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - ushort d[sizeof(qptrdiff)/sizeof(ushort)]; - }; - inline ushort *data() { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } - inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } + + qptrdiff offset; + + inline ushort *data() { return reinterpret_cast(reinterpret_cast(this) + offset); } + inline const ushort *data() const { return reinterpret_cast(reinterpret_cast(this) + offset); } }; template struct QStaticStringData; @@ -126,7 +125,7 @@ template struct QStaticStringData # define QStringLiteral(str) ([]() -> QStaticStringDataPtr { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ static const QStaticStringData qstring_literal = \ - { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ QStaticStringDataPtr holder = { &qstring_literal }; \ return holder; }()) @@ -139,7 +138,7 @@ template struct QStaticStringData __extension__ ({ \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ static const QStaticStringData qstring_literal = \ - { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ QStaticStringDataPtr holder = { &qstring_literal }; \ holder; }) # endif @@ -705,7 +704,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) realloc(); } +{ if (d->ref.isShared() || (d->offset != sizeof(QStringData))) realloc(); } inline bool QString::isDetached() const { return !d->ref.isShared(); } inline QString &QString::operator=(const QLatin1String &s) diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index e3808244d3..ba583f3cb8 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -5150,7 +5150,7 @@ void tst_QString::literals() QVERIFY(str.length() == 4); QVERIFY(str == QLatin1String("abcd")); QVERIFY(str.data_ptr()->ref.isStatic()); - QVERIFY(str.data_ptr()->offset == 0); + QVERIFY(str.data_ptr()->offset == sizeof(QStringData)); const QChar *s = str.constData(); QString str2 = str; From a1621d235dad75b8042f4e13712e1ea3440bf717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 17 Feb 2012 12:10:03 +0100 Subject: [PATCH 52/74] Change QVector's in-memory data layout The new layout matches that of QByteArrayData and QStringData, except for offset which is measured from the beginning of QVectorData, whereas in those classes it (still?) references the end of the header data. The new layout uses an extra member for storing an offset into the data, which will allow introducing QVector::fromRawData, similar to the same functionality already existing in QString and QByteArray. By not using an actual array to index array members, we also steer clear of GCC bug #43247: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247 Change-Id: I408915aacadf616b4633bbbf5cae1fc19e415087 Reviewed-by: Thiago Macieira --- src/corelib/tools/qvector.h | 255 +++++++++--------- .../corelib/tools/qvector/qrawvector.h | 11 +- 2 files changed, 129 insertions(+), 137 deletions(-) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 83b1910450..6b41d3dc62 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -65,16 +65,13 @@ QT_BEGIN_NAMESPACE struct Q_CORE_EXPORT QVectorData { QtPrivate::RefCount ref; - int alloc; int size; -#if defined(Q_PROCESSOR_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) - // workaround for bug in gcc 3.4.2 - uint capacity; - uint reserved; -#else - uint capacity : 1; - uint reserved : 31; -#endif + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; + + void* data() { return reinterpret_cast(this) + this->offset; } static const QVectorData shared_null; static QVectorData *allocate(int size, int alignment); @@ -84,12 +81,12 @@ struct Q_CORE_EXPORT QVectorData }; template -struct QVectorTypedData : private QVectorData -{ // private inheritance as we must not access QVectorData member thought QVectorTypedData - // as this would break strict aliasing rules. (in the case of shared_null) - T array[1]; +struct QVectorTypedData : QVectorData +{ + T* begin() { return reinterpret_cast(this->data()); } + T* end() { return begin() + this->size; } - static inline void free(QVectorTypedData *x, int alignment) { QVectorData::free(static_cast(x), alignment); } + static QVectorTypedData *sharedNull() { return static_cast(const_cast(&QVectorData::shared_null)); } }; class QRegion; @@ -98,19 +95,10 @@ template class QVector { typedef QVectorTypedData Data; - union { - QVectorData *d; -#if defined(Q_CC_SUN) && (__SUNPRO_CC <= 0x550) - QVectorTypedData *p; -#else - Data *p; -#endif - }; + Data *d; public: - // ### Qt 5: Consider making QVector non-shared to get at least one - // "really fast" container. See tests/benchmarks/corelib/tools/qvector/ - inline QVector() : d(const_cast(&QVectorData::shared_null)) { } + inline QVector() : d(Data::sharedNull()) { } explicit QVector(int size); QVector(int size, const T &t); inline QVector(const QVector &v) @@ -118,19 +106,19 @@ public: if (v.d->ref.ref()) { d = v.d; } else { - d = const_cast(&QVectorData::shared_null); + d = Data::sharedNull(); realloc(0, v.d->alloc); - qCopy(v.p->array, v.p->array + v.d->size, p->array); + qCopy(v.d->begin(), v.d->end(), d->begin()); d->size = v.d->size; - d->capacity = v.d->capacity; + d->capacityReserved = v.d->capacityReserved; } } - inline ~QVector() { if (!d->ref.deref()) free(p); } + inline ~QVector() { if (!d->ref.deref()) free(d); } QVector &operator=(const QVector &v); #ifdef Q_COMPILER_RVALUE_REFS inline QVector operator=(QVector &&other) - { qSwap(p, other.p); return *this; } + { qSwap(d, other.d); return *this; } #endif inline void swap(QVector &other) { qSwap(d, other.d); } #ifdef Q_COMPILER_INITIALIZER_LISTS @@ -147,7 +135,7 @@ public: inline int capacity() const { return d->alloc; } void reserve(int size); - inline void squeeze() { realloc(d->size, d->size); d->capacity = 0; } + inline void squeeze() { realloc(d->size, d->size); d->capacityReserved = 0; } inline void detach() { if (!isDetached()) detach_helper(); } inline bool isDetached() const { return !d->ref.isShared(); } @@ -157,15 +145,15 @@ public: return; if (!sharable) detach(); - if (d != &QVectorData::shared_null) + if (d != Data::sharedNull()) d->ref.setSharable(sharable); } inline bool isSharedWith(const QVector &other) const { return d == other.d; } - inline T *data() { detach(); return p->array; } - inline const T *data() const { return p->array; } - inline const T *constData() const { return p->array; } + inline T *data() { detach(); return d->begin(); } + inline const T *data() const { return d->begin(); } + inline const T *constData() const { return d->begin(); } void clear(); const T &at(int i) const; @@ -258,12 +246,12 @@ public: typedef T* iterator; typedef const T* const_iterator; #endif - inline iterator begin() { detach(); return p->array; } - inline const_iterator begin() const { return p->array; } - inline const_iterator constBegin() const { return p->array; } - inline iterator end() { detach(); return p->array + d->size; } - inline const_iterator end() const { return p->array + d->size; } - inline const_iterator constEnd() const { return p->array + d->size; } + inline iterator begin() { detach(); return d->begin(); } + inline const_iterator begin() const { return d->begin(); } + inline const_iterator constBegin() const { return d->begin(); } + inline iterator end() { detach(); return d->end(); } + inline const_iterator end() const { return d->end(); } + inline const_iterator constEnd() const { return d->end(); } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } iterator erase(iterator begin, iterator end); @@ -328,19 +316,21 @@ private: friend class QRegion; // Optimization for QRegion::rects() void detach_helper(); - QVectorData *malloc(int alloc); + Data *malloc(int alloc); void realloc(int size, int alloc); void free(Data *d); + class AlignmentDummy { QVectorData header; T array[1]; }; + static Q_DECL_CONSTEXPR int offsetOfTypedData() { - // (non-POD)-safe offsetof(Data, array) + // (non-POD)-safe offsetof(AlignmentDummy, array) return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return Q_ALIGNOF(Data); + return Q_ALIGNOF(AlignmentDummy); #else return sizeof(void *); #endif @@ -352,10 +342,10 @@ void QVector::detach_helper() { realloc(d->size, d->alloc); } template void QVector::reserve(int asize) -{ if (asize > d->alloc) realloc(d->size, asize); if (isDetached()) d->capacity = 1; } +{ if (asize > d->alloc) realloc(d->size, asize); if (isDetached()) d->capacityReserved = 1; } template void QVector::resize(int asize) -{ realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? +{ realloc(asize, (asize > d->alloc || (!d->capacityReserved && asize < d->size && asize < (d->alloc >> 1))) ? QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) : d->alloc); } template @@ -364,11 +354,11 @@ inline void QVector::clear() template inline const T &QVector::at(int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector::at", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template inline const T &QVector::operator[](int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector::operator[]", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template inline T &QVector::operator[](int i) { Q_ASSERT_X(i >= 0 && i < d->size, "QVector::operator[]", "index out of range"); @@ -412,11 +402,11 @@ QVector &QVector::operator=(const QVector &v) } template -inline QVectorData *QVector::malloc(int aalloc) +inline typename QVector::Data *QVector::malloc(int aalloc) { QVectorData *vectordata = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(vectordata); - return vectordata; + return static_cast(vectordata); } template @@ -425,14 +415,15 @@ QVector::QVector(int asize) d = malloc(asize); d->ref.initializeOwned(); d->alloc = d->size = asize; - d->capacity = false; + d->capacityReserved = false; + d->offset = offsetOfTypedData(); if (QTypeInfo::isComplex) { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) new (--i) T; } else { - qMemSet(p->array, 0, asize * sizeof(T)); + qMemSet(d->begin(), 0, asize * sizeof(T)); } } @@ -442,9 +433,10 @@ QVector::QVector(int asize, const T &t) d = malloc(asize); d->ref.initializeOwned(); d->alloc = d->size = asize; - d->capacity = false; - T* i = p->array + d->size; - while (i != p->array) + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + T* i = d->end(); + while (i != d->begin()) new (--i) T(t); } @@ -455,11 +447,11 @@ QVector::QVector(std::initializer_list args) d = malloc(int(args.size())); d->ref.initializeOwned(); d->alloc = d->size = int(args.size()); - d->capacity = false; - T* i = p->array + d->size; + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + T* i = d->end(); auto it = args.end(); - while (i != p->array) - new (--i) T(*(--it)); + while (i != d->begin()) } #endif @@ -467,14 +459,12 @@ template void QVector::free(Data *x) { if (QTypeInfo::isComplex) { - T* b = x->array; - union { QVectorData *d; Data *p; } u; - u.p = x; - T* i = b + u.d->size; + T* b = x->begin(); + T* i = b + x->size; while (i-- != b) i->~T(); } - x->free(x, alignOfTypedData()); + Data::free(x, alignOfTypedData()); } template @@ -483,14 +473,13 @@ void QVector::realloc(int asize, int aalloc) Q_ASSERT(asize <= aalloc); T *pOld; T *pNew; - union { QVectorData *d; Data *p; } x; - x.d = d; + Data *x = d; if (QTypeInfo::isComplex && asize < d->size && isDetached()) { // call the destructor on all objects that need to be // destroyed when shrinking - pOld = p->array + d->size; - pNew = p->array + asize; + pOld = d->begin() + d->size; + pNew = d->begin() + asize; while (asize < d->size) { (--pOld)->~T(); d->size--; @@ -500,66 +489,66 @@ void QVector::realloc(int asize, int aalloc) if (aalloc != d->alloc || !isDetached()) { // (re)allocate memory if (QTypeInfo::isStatic) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); - x.d->size = 0; + x = malloc(aalloc); + Q_CHECK_PTR(x); + x->size = 0; } else if (!isDetached()) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); + x = malloc(aalloc); + Q_CHECK_PTR(x); if (QTypeInfo::isComplex) { - x.d->size = 0; + x->size = 0; } else { - ::memcpy(x.p, p, offsetOfTypedData() + qMin(aalloc, d->alloc) * sizeof(T)); - x.d->size = d->size; + ::memcpy(x, d, offsetOfTypedData() + qMin(uint(aalloc), d->alloc) * sizeof(T)); + x->size = d->size; } } else { QT_TRY { QVectorData *mem = QVectorData::reallocate(d, offsetOfTypedData() + aalloc * sizeof(T), offsetOfTypedData() + d->alloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); - x.d = d = mem; - x.d->size = d->size; + x = d = static_cast(mem); + x->size = d->size; } QT_CATCH (const std::bad_alloc &) { if (aalloc > d->alloc) // ignore the error in case we are just shrinking. QT_RETHROW; } } - x.d->ref.initializeOwned(); - x.d->alloc = aalloc; - x.d->capacity = d->capacity; - x.d->reserved = 0; + x->ref.initializeOwned(); + x->alloc = aalloc; + x->capacityReserved = d->capacityReserved; + x->offset = offsetOfTypedData(); } if (QTypeInfo::isComplex) { QT_TRY { - pOld = p->array + x.d->size; - pNew = x.p->array + x.d->size; + pOld = d->begin() + x->size; + pNew = x->begin() + x->size; // copy objects from the old array into the new array const int toMove = qMin(asize, d->size); - while (x.d->size < toMove) { + while (x->size < toMove) { new (pNew++) T(*pOld++); - x.d->size++; + x->size++; } // construct all new objects when growing - while (x.d->size < asize) { + while (x->size < asize) { new (pNew++) T; - x.d->size++; + x->size++; } } QT_CATCH (...) { - free(x.p); + free(x); QT_RETHROW; } - } else if (asize > x.d->size) { + } else if (asize > x->size) { // initialize newly allocated memory to 0 - qMemSet(x.p->array + x.d->size, 0, (asize - x.d->size) * sizeof(T)); + qMemSet(x->end(), 0, (asize - x->size) * sizeof(T)); } - x.d->size = asize; + x->size = asize; - if (d != x.d) { + if (d != x) { if (!d->ref.deref()) - free(p); - d = x.d; + free(d); + d = x; } } @@ -569,12 +558,12 @@ Q_OUTOFLINE_TEMPLATE T QVector::value(int i) const if (i < 0 || i >= d->size) { return T(); } - return p->array[i]; + return d->begin()[i]; } template Q_OUTOFLINE_TEMPLATE T QVector::value(int i, const T &defaultValue) const { - return ((i < 0 || i >= d->size) ? defaultValue : p->array[i]); + return ((i < 0 || i >= d->size) ? defaultValue : d->begin()[i]); } template @@ -586,14 +575,14 @@ void QVector::append(const T &t) QVectorData::grow(offsetOfTypedData(), d->size + 1, sizeof(T)) : d->alloc); if (QTypeInfo::isComplex) - new (p->array + d->size) T(copy); + new (d->end()) T(copy); else - p->array[d->size] = copy; + *d->end() = copy; } else { if (QTypeInfo::isComplex) - new (p->array + d->size) T(t); + new (d->end()) T(t); else - p->array[d->size] = t; + *d->end() = t; } ++d->size; } @@ -601,26 +590,26 @@ void QVector::append(const T &t) template typename QVector::iterator QVector::insert(iterator before, size_type n, const T &t) { - int offset = int(before - p->array); + int offset = int(before - d->begin()); if (n != 0) { const T copy(t); if (!isDetached() || d->size + n > d->alloc) realloc(d->size, QVectorData::grow(offsetOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo::isStatic) { - T *b = p->array + d->size; - T *i = p->array + d->size + n; + T *b = d->end(); + T *i = d->end() + n; while (i != b) new (--i) T; - i = p->array + d->size; + i = d->end(); T *j = i + n; - b = p->array + offset; + b = d->begin() + offset; while (i != b) *--j = *--i; i = b+n; while (i != b) *--i = copy; } else { - T *b = p->array + offset; + T *b = d->begin() + offset; T *i = b + n; memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) @@ -628,29 +617,29 @@ typename QVector::iterator QVector::insert(iterator before, size_type n, c } d->size += n; } - return p->array + offset; + return d->begin() + offset; } template typename QVector::iterator QVector::erase(iterator abegin, iterator aend) { - int f = int(abegin - p->array); - int l = int(aend - p->array); + int f = int(abegin - d->begin()); + int l = int(aend - d->begin()); int n = l - f; detach(); if (QTypeInfo::isComplex) { - qCopy(p->array+l, p->array+d->size, p->array+f); - T *i = p->array+d->size; - T* b = p->array+d->size-n; + qCopy(d->begin()+l, d->end(), d->begin()+f); + T *i = d->end(); + T* b = d->end()-n; while (i != b) { --i; i->~T(); } } else { - memmove(p->array + f, p->array + l, (d->size-l)*sizeof(T)); + memmove(d->begin() + f, d->begin() + l, (d->size-l)*sizeof(T)); } d->size -= n; - return p->array + f; + return d->begin() + f; } template @@ -660,9 +649,9 @@ bool QVector::operator==(const QVector &v) const return false; if (d == v.d) return true; - T* b = p->array; + T* b = d->begin(); T* i = b + d->size; - T* j = v.p->array + d->size; + T* j = v.d->end(); while (i != b) if (!(*--i == *--j)) return false; @@ -675,8 +664,8 @@ QVector &QVector::fill(const T &from, int asize) const T copy(from); resize(asize < 0 ? d->size : asize); if (d->size) { - T *i = p->array + d->size; - T *b = p->array; + T *i = d->end(); + T *b = d->begin(); while (i != b) *--i = copy; } @@ -689,9 +678,9 @@ QVector &QVector::operator+=(const QVector &l) int newSize = d->size + l.d->size; realloc(d->size, newSize); - T *w = p->array + newSize; - T *i = l.p->array + l.d->size; - T *b = l.p->array; + T *w = d->begin() + newSize; + T *i = l.d->end(); + T *b = l.d->begin(); while (i != b) { if (QTypeInfo::isComplex) new (--w) T(*--i); @@ -708,11 +697,11 @@ int QVector::indexOf(const T &t, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - T* n = p->array + from - 1; - T* e = p->array + d->size; + T* n = d->begin() + from - 1; + T* e = d->end(); while (++n != e) if (*n == t) - return n - p->array; + return n - d->begin(); } return -1; } @@ -725,8 +714,8 @@ int QVector::lastIndexOf(const T &t, int from) const else if (from >= d->size) from = d->size-1; if (from >= 0) { - T* b = p->array; - T* n = p->array + from + 1; + T* b = d->begin(); + T* n = d->begin() + from + 1; while (n != b) { if (*--n == t) return n - b; @@ -738,8 +727,8 @@ int QVector::lastIndexOf(const T &t, int from) const template bool QVector::contains(const T &t) const { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) return true; @@ -750,8 +739,8 @@ template int QVector::count(const T &t) const { int c = 0; - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) ++c; diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index 1342513a7f..0fdfa86f1d 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE template class QRawVector { - struct Data : QVectorData { T array[1]; }; + typedef QVectorTypedData Data; T *m_begin; int m_size; @@ -265,15 +265,17 @@ private: void realloc(int size, int alloc, bool ref); void free(T *begin, int size); + class AlignmentDummy { QVectorData header; T array[1]; }; + static Q_DECL_CONSTEXPR int offsetOfTypedData() { - // (non-POD)-safe offsetof(Data, array) + // (non-POD)-safe offsetof(AlignmentDummy, array) return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return Q_ALIGNOF(Data); + return Q_ALIGNOF(AlignmentDummy); #else return sizeof(void *); #endif @@ -286,7 +288,8 @@ public: d->ref.initializeOwned(); d->alloc = m_alloc; d->size = m_size; - d->capacity = 0; + d->capacityReserved = 0; + d->offset = offsetOfTypedData(); QVector v; *reinterpret_cast(&v) = d; From 91e20fff873d8d352dd94fc94b0f54ab0feb3aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 17 Feb 2012 00:29:12 +0100 Subject: [PATCH 53/74] SimpleVector: don't assert when reserving on empty Change-Id: I09ac235085e645c8149c153653377252fef6fa3d Reviewed-by: Thiago Macieira --- .../corelib/tools/qarraydata/simplevector.h | 5 +- .../tools/qarraydata/tst_qarraydata.cpp | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index b16025b34a..a0a9b5f764 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -156,9 +156,10 @@ public: } } - SimpleVector detached(Data::allocate(n, + SimpleVector detached(Data::allocate(qMax(n, size()), d->detachFlags() | Data::CapacityReserved)); - detached.d->copyAppend(constBegin(), constEnd()); + if (size()) + detached.d->copyAppend(constBegin(), constEnd()); detached.swap(*this); } diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 561491da00..0112d714f6 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -72,6 +72,8 @@ private slots: void sharedNullEmpty(); void staticData(); void simpleVector(); + void simpleVectorReserve_data(); + void simpleVectorReserve(); void allocate_data(); void allocate(); void alignment_data(); @@ -529,6 +531,66 @@ void tst_QArrayData::simpleVector() } } +Q_DECLARE_METATYPE(SimpleVector) + +void tst_QArrayData::simpleVectorReserve_data() +{ + QTest::addColumn >("vector"); + QTest::addColumn("capacity"); + QTest::addColumn("size"); + + QTest::newRow("null") << SimpleVector() << size_t(0) << size_t(0); + QTest::newRow("empty") << SimpleVector(0, 42) << size_t(0) << size_t(0); + QTest::newRow("non-empty") << SimpleVector(5, 42) << size_t(5) << size_t(5); + + static const QStaticArrayData 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 p = { + static_cast *>( + const_cast(&array.header)) }; + + QTest::newRow("static") << SimpleVector(p) << size_t(0) << size_t(15); + QTest::newRow("raw-data") << SimpleVector::fromRawData(array.data, 15) << size_t(0) << size_t(15); +} + +void tst_QArrayData::simpleVectorReserve() +{ + QFETCH(SimpleVector, vector); + QFETCH(size_t, capacity); + QFETCH(size_t, size); + + QVERIFY(!capacity || capacity >= size); + + QCOMPARE(vector.capacity(), capacity); + QCOMPARE(vector.size(), size); + + const SimpleVector 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) From 57aba47cdeaf2a107822a683c280b3929c5ee0e6 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 17 Feb 2012 22:45:08 +0100 Subject: [PATCH 54/74] Port badxml autotest to QMetaObjectBuilder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The meta-object format is going to change for Qt5. Use QMOB to insulate the badxml test from such changes. (It just so happens that the QFAIL("a failure") statement is still on line 109 after the refactoring, so the expected_badxml.* files' location tags did not have to be changed.) Change-Id: I04421d13c4df71c8004fa71cafc4823a59079a41 Reviewed-by: Thiago Macieira Reviewed-by: Rohan McGovern Reviewed-by: Jason McDonald (cherry picked from commit 12520e83009fb8bd694d9768801875558bcc6321) Reviewed-by: João Abecasis --- .../auto/testlib/selftests/badxml/badxml.pro | 2 +- .../testlib/selftests/badxml/tst_badxml.cpp | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/auto/testlib/selftests/badxml/badxml.pro b/tests/auto/testlib/selftests/badxml/badxml.pro index 5eaa1c3216..7b3b0f701c 100644 --- a/tests/auto/testlib/selftests/badxml/badxml.pro +++ b/tests/auto/testlib/selftests/badxml/badxml.pro @@ -1,5 +1,5 @@ SOURCES += tst_badxml.cpp -QT = core testlib +QT = core-private testlib mac:CONFIG -= app_bundle CONFIG -= debug_and_release_target diff --git a/tests/auto/testlib/selftests/badxml/tst_badxml.cpp b/tests/auto/testlib/selftests/badxml/tst_badxml.cpp index 1a143e5243..2fb9e5ea90 100644 --- a/tests/auto/testlib/selftests/badxml/tst_badxml.cpp +++ b/tests/auto/testlib/selftests/badxml/tst_badxml.cpp @@ -42,6 +42,7 @@ #include #include +#include /* This test makes a testlog containing lots of characters which have a special meaning in @@ -73,27 +74,26 @@ class EmptyClass : public tst_BadXml class tst_BadXmlSub : public tst_BadXml { public: + tst_BadXmlSub() + : className("tst_BadXml"), mo(0) {} + ~tst_BadXmlSub() { qFree(mo); } + const QMetaObject* metaObject() const; - static char const* className; + QByteArray className; +private: + QMetaObject *mo; }; -char const* tst_BadXmlSub::className = "tst_BadXml"; const QMetaObject* tst_BadXmlSub::metaObject() const { - const QMetaObject& empty = EmptyClass::staticMetaObject; - static QMetaObject mo = { - { empty.d.superdata, empty.d.stringdata, empty.d.data, empty.d.extradata } - }; - static char currentClassName[1024]; - qstrcpy(currentClassName, className); - int len = qstrlen(className); - currentClassName[len] = 0; - currentClassName[len+1] = 0; - - mo.d.stringdata = currentClassName; - - return &mo; + if (!mo || (mo->className() != className)) { + qFree(mo); + QMetaObjectBuilder builder(&EmptyClass::staticMetaObject); + builder.setClassName(className); + const_cast(this)->mo = builder.toMetaObject(); + } + return mo; } /* From 81dc01d43f2c4ccf43c0b7cae956df7c2c71ff01 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Thu, 23 Feb 2012 13:52:16 +0100 Subject: [PATCH 55/74] Bump the moc output revision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit aee1f6cc413f56bf4962324799ee3887c3dd037f changed the values of some built-in meta-type ids. Since the ids of built-in types are directly encoded -- not as the symbolic QMetaType::Type name, but as a raw integer -- in the flags for meta-properties, the moc output prior to that change is incompatible with the current output. Change-Id: I970484825137a4f19c80726cfe2024e741e3e879 Reviewed-by: Jędrzej Nowacki Reviewed-by: Roberto Raggi Reviewed-by: Thiago Macieira Reviewed-by: Olivier Goffart (cherry picked from commit ca028e1fe07f2fb268d1da73eeaccb0f7f264a04) Reviewed-by: João Abecasis --- src/corelib/kernel/qobjectdefs.h | 2 +- src/tools/moc/outputrevision.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 0b1fa8839f..ec51251531 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -54,7 +54,7 @@ class QByteArray; class QString; #ifndef Q_MOC_OUTPUT_REVISION -#define Q_MOC_OUTPUT_REVISION 63 +#define Q_MOC_OUTPUT_REVISION 64 #endif // The following macros are our "extensions" to C++ diff --git a/src/tools/moc/outputrevision.h b/src/tools/moc/outputrevision.h index 15661a43aa..2ce5b9b765 100644 --- a/src/tools/moc/outputrevision.h +++ b/src/tools/moc/outputrevision.h @@ -43,6 +43,6 @@ #define OUTPUTREVISION_H // if the output revision changes, you MUST change it in qobjectdefs.h too -enum { mocOutputRevision = 63 }; // moc format output revision +enum { mocOutputRevision = 64 }; // moc format output revision #endif // OUTPUTREVISION_H From 7da3a61b5fd5cc726f8fd62691aa5f84c7929800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Mon, 27 Feb 2012 13:52:10 +0100 Subject: [PATCH 56/74] Fix signed/unsigned mismatch warnings Introduced by the change of d->alloc to unsigned, in a1621d23. Change-Id: I9e6d7fc2efbf5228c4e59c7128b8c89cf284db24 Reviewed-by: Thiago Macieira Reviewed-by: Kent Hansen --- src/corelib/tools/qvector.h | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 6b41d3dc62..8c686a2547 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -107,7 +107,7 @@ public: d = v.d; } else { d = Data::sharedNull(); - realloc(0, v.d->alloc); + realloc(0, int(v.d->alloc)); qCopy(v.d->begin(), v.d->end(), d->begin()); d->size = v.d->size; d->capacityReserved = v.d->capacityReserved; @@ -133,7 +133,7 @@ public: void resize(int size); - inline int capacity() const { return d->alloc; } + inline int capacity() const { return int(d->alloc); } void reserve(int size); inline void squeeze() { realloc(d->size, d->size); d->capacityReserved = 0; } @@ -339,15 +339,15 @@ private: template void QVector::detach_helper() -{ realloc(d->size, d->alloc); } +{ realloc(d->size, int(d->alloc)); } template void QVector::reserve(int asize) -{ if (asize > d->alloc) realloc(d->size, asize); if (isDetached()) d->capacityReserved = 1; } +{ if (asize > int(d->alloc)) realloc(d->size, asize); if (isDetached()) d->capacityReserved = 1; } template void QVector::resize(int asize) -{ realloc(asize, (asize > d->alloc || (!d->capacityReserved && asize < d->size && asize < (d->alloc >> 1))) ? +{ realloc(asize, (asize > int(d->alloc) || (!d->capacityReserved && asize < d->size && asize < int(d->alloc >> 1))) ? QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) - : d->alloc); } + : int(d->alloc)); } template inline void QVector::clear() { *this = QVector(); } @@ -414,7 +414,8 @@ QVector::QVector(int asize) { d = malloc(asize); d->ref.initializeOwned(); - d->alloc = d->size = asize; + d->size = asize; + d->alloc = uint(d->size); d->capacityReserved = false; d->offset = offsetOfTypedData(); if (QTypeInfo::isComplex) { @@ -432,7 +433,8 @@ QVector::QVector(int asize, const T &t) { d = malloc(asize); d->ref.initializeOwned(); - d->alloc = d->size = asize; + d->size = asize; + d->alloc = uint(d->size); d->capacityReserved = false; d->offset = offsetOfTypedData(); T* i = d->end(); @@ -446,7 +448,8 @@ QVector::QVector(std::initializer_list args) { d = malloc(int(args.size())); d->ref.initializeOwned(); - d->alloc = d->size = int(args.size()); + d->size = int(args.size()); + d->alloc = uint(d->size); d->capacityReserved = false; d->offset = offsetOfTypedData(); T* i = d->end(); @@ -486,7 +489,7 @@ void QVector::realloc(int asize, int aalloc) } } - if (aalloc != d->alloc || !isDetached()) { + if (aalloc != int(d->alloc) || !isDetached()) { // (re)allocate memory if (QTypeInfo::isStatic) { x = malloc(aalloc); @@ -509,12 +512,12 @@ void QVector::realloc(int asize, int aalloc) x = d = static_cast(mem); x->size = d->size; } QT_CATCH (const std::bad_alloc &) { - if (aalloc > d->alloc) // ignore the error in case we are just shrinking. + if (aalloc > int(d->alloc)) // ignore the error in case we are just shrinking. QT_RETHROW; } } x->ref.initializeOwned(); - x->alloc = aalloc; + x->alloc = uint(aalloc); x->capacityReserved = d->capacityReserved; x->offset = offsetOfTypedData(); } @@ -569,11 +572,11 @@ Q_OUTOFLINE_TEMPLATE T QVector::value(int i, const T &defaultValue) const template void QVector::append(const T &t) { - if (!isDetached() || d->size + 1 > d->alloc) { + if (!isDetached() || d->size + 1 > int(d->alloc)) { const T copy(t); - realloc(d->size, (d->size + 1 > d->alloc) ? + realloc(d->size, (d->size + 1 > int(d->alloc)) ? QVectorData::grow(offsetOfTypedData(), d->size + 1, sizeof(T)) - : d->alloc); + : int(d->alloc)); if (QTypeInfo::isComplex) new (d->end()) T(copy); else @@ -593,7 +596,7 @@ typename QVector::iterator QVector::insert(iterator before, size_type n, c int offset = int(before - d->begin()); if (n != 0) { const T copy(t); - if (!isDetached() || d->size + n > d->alloc) + if (!isDetached() || d->size + n > int(d->alloc)) realloc(d->size, QVectorData::grow(offsetOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo::isStatic) { T *b = d->end(); From 02c1863485ec26ab6756fd7ec85d60d3bce68eb3 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 21 Feb 2012 19:50:50 +0100 Subject: [PATCH 57/74] Update QDBusAbstractConnector to meta-object revision 7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate the moc output so that it's in sync with the latest moc. Change-Id: I86001f88bbc0127fc26414cf6eef512cd6c71e44 Reviewed-by: Thiago Macieira Reviewed-by: Jędrzej Nowacki --- src/dbus/qdbusabstractadaptor.cpp | 57 +++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/src/dbus/qdbusabstractadaptor.cpp b/src/dbus/qdbusabstractadaptor.cpp index 7bdd947a37..3ba8acca82 100644 --- a/src/dbus/qdbusabstractadaptor.cpp +++ b/src/dbus/qdbusabstractadaptor.cpp @@ -323,10 +323,38 @@ void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void ** // modify carefully: this has been hand-edited! // the relaySlot slot gets called with the void** array +struct qt_meta_stringdata_QDBusAdaptorConnector_t { + QByteArrayData data[10]; + char stringdata[96]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) { \ + Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ + offsetof(qt_meta_stringdata_QDBusAdaptorConnector_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + } +static const qt_meta_stringdata_QDBusAdaptorConnector_t qt_meta_stringdata_QDBusAdaptorConnector = { + { +QT_MOC_LITERAL(0, 0, 21), +QT_MOC_LITERAL(1, 22, 11), +QT_MOC_LITERAL(2, 34, 0), +QT_MOC_LITERAL(3, 35, 3), +QT_MOC_LITERAL(4, 39, 18), +QT_MOC_LITERAL(5, 58, 10), +QT_MOC_LITERAL(6, 69, 3), +QT_MOC_LITERAL(7, 73, 4), +QT_MOC_LITERAL(8, 78, 9), +QT_MOC_LITERAL(9, 88, 6) + }, + "QDBusAdaptorConnector\0relaySignal\0\0" + "obj\0const QMetaObject*\0metaObject\0sid\0" + "args\0relaySlot\0polish\0" +}; +#undef QT_MOC_LITERAL + static const uint qt_meta_data_QDBusAdaptorConnector[] = { // content: - 6, // revision + 7, // revision 0, // classname 0, 0, // classinfo 3, 14, // methods @@ -336,22 +364,23 @@ static const uint qt_meta_data_QDBusAdaptorConnector[] = { 0, // flags 1, // signalCount - // signals: signature, parameters, type, tag, flags - 47, 23, 22, 22, 0x05, + // signals: name, argc, parameters, tag, flags + 1, 4, 29, 2, 0x05, - // slots: signature, parameters, type, tag, flags - 105, 22, 22, 22, 0x0a, - 117, 22, 22, 22, 0x0a, + // slots: name, argc, parameters, tag, flags + 8, 0, 38, 2, 0x0a, + 9, 0, 39, 2, 0x0a, + + // signals: parameters + QMetaType::Void, QMetaType::QObjectStar, 0x80000000 | 4, QMetaType::Int, QMetaType::QVariantList, 3, 5, 6, 7, + + // slots: parameters + QMetaType::Void, + QMetaType::Void, 0 // eod }; -static const char qt_meta_stringdata_QDBusAdaptorConnector[] = { - "QDBusAdaptorConnector\0\0obj,metaObject,sid,args\0" - "relaySignal(QObject*,const QMetaObject*,int,QVariantList)\0" - "relaySlot()\0polish()\0" -}; - void QDBusAdaptorConnector::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { @@ -371,7 +400,7 @@ const QMetaObjectExtraData QDBusAdaptorConnector::staticMetaObjectExtraData = { }; const QMetaObject QDBusAdaptorConnector::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector, + { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector.data, qt_meta_data_QDBusAdaptorConnector, &staticMetaObjectExtraData } }; @@ -383,7 +412,7 @@ const QMetaObject *QDBusAdaptorConnector::metaObject() const void *QDBusAdaptorConnector::qt_metacast(const char *_clname) { if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector)) + if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector.stringdata)) return static_cast(const_cast< QDBusAdaptorConnector*>(this)); return QObject::qt_metacast(_clname); } From 3f7a222414fc9d3e9f2e2cfdd05f33740c5afb7e Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sat, 18 Feb 2012 20:36:06 +0100 Subject: [PATCH 58/74] Change the representation of meta-object string data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to and including meta-object revision 6, string data have been stored as 0-terminated C-style strings, that were made directly accessible as const char pointers through the public API (QMetaMethod and friends). This commit changes moc to generate an array of QByteArrayData instead, and adapts the QObject kernel accordingly. Generating an array of QByteArrayData (byte array literals) means that the strings can now be returned from public (or private) API as QByteArrays, rather than const char *, with zero allocation or copying. Also, the string length is now computed at compile time (it's part of the QByteArrayData). This commit only changes the internal representation, and does not affect existing public API. The actual (C) string data that the byte array literals reference still consists of zero-terminated strings. The benefit of having the QByteArrayData array will only become apparent in the upcoming meta-object data format change, which changes the format of property and method descriptors. Support for the old meta-object string data format was kept; the codepaths for old revisions (6 and below) will be removed in a separate commit, once all the other meta-object changes are done and affected code has been adapted accordingly. Task-number: QTBUG-24154 Change-Id: I4ec3b363bbc31b8192e5d8915ef091c442c2efad Reviewed-by: Lars Knoll Reviewed-by: Olivier Goffart Reviewed-by: João Abecasis Reviewed-by: Bradley T. Hughes --- src/corelib/kernel/qmetaobject.cpp | 114 +++++--- src/corelib/kernel/qmetaobject_p.h | 5 +- src/corelib/kernel/qobject.cpp | 4 +- src/corelib/kernel/qobjectdefs.h | 8 +- src/tools/moc/generator.cpp | 252 +++++++++++++----- src/tools/moc/generator.h | 7 +- src/tools/moc/moc.cpp | 1 + src/tools/moc/outputrevision.h | 2 +- .../kernel/qobject/moc_oldnormalizeobject.cpp | 2 +- 9 files changed, 274 insertions(+), 121 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index d53ba707f7..ce30e34f73 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -146,6 +146,43 @@ QT_BEGIN_NAMESPACE static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast(data); } +static inline const QByteArrayData &stringData(const QMetaObject *mo, int index) +{ + Q_ASSERT(priv(mo->d.data)->revision >= 7); + const QByteArrayData &data = mo->d.stringdata[index]; + Q_ASSERT(data.ref.isStatic()); + Q_ASSERT(data.alloc == 0); + Q_ASSERT(data.capacityReserved == 0); + Q_ASSERT(data.size >= 0); + return data; +} + +static inline const char *legacyString(const QMetaObject *mo, int index) +{ + Q_ASSERT(priv(mo->d.data)->revision <= 6); + return reinterpret_cast(mo->d.stringdata) + index; +} + +static inline const char *rawStringData(const QMetaObject *mo, int index) +{ + if (priv(mo->d.data)->revision >= 7) + return stringData(mo, index).data(); + else + return legacyString(mo, index); +} + +const char *QMetaObjectPrivate::rawStringData(const QMetaObject *mo, int index) +{ + return QT_PREPEND_NAMESPACE(rawStringData)(mo, index); +} + +static inline int stringSize(const QMetaObject *mo, int index) +{ + if (priv(mo->d.data)->revision >= 7) + return stringData(mo, index).size; + else + return qstrlen(legacyString(mo, index)); +} /*! \since 4.5 @@ -249,12 +286,14 @@ int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) } /*! - \fn const char *QMetaObject::className() const - Returns the class name. \sa superClass() */ +const char *QMetaObject::className() const +{ + return rawStringData(this, 0); +} /*! \fn QMetaObject *QMetaObject::superClass() const @@ -307,7 +346,7 @@ const QObject *QMetaObject::cast(const QObject *obj) const */ QString QMetaObject::tr(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::CodecForTr, n); + return QCoreApplication::translate(rawStringData(this, 0), s, c, QCoreApplication::CodecForTr, n); } /*! @@ -315,7 +354,7 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const */ QString QMetaObject::trUtf8(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8, n); + return QCoreApplication::translate(rawStringData(this, 0), s, c, QCoreApplication::UnicodeUTF8, n); } #endif // QT_NO_TRANSLATION @@ -513,7 +552,7 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, ? (priv(m->d.data)->signalCount) : 0; if (!normalizeStringData) { for (; i >= end; --i) { - const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]; + const char *stringdata = rawStringData(m, m->d.data[priv(m->d.data)->methodData + 5*i]); if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { *baseObject = m; return i; @@ -521,7 +560,7 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, } } else if (priv(m->d.data)->revision < 5) { for (; i >= end; --i) { - const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); + const char *stringdata = legacyString(m, m->d.data[priv(m->d.data)->methodData + 5 * i]); const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); if (normalizedSignature == method) { *baseObject = m; @@ -549,7 +588,7 @@ int QMetaObject::indexOfConstructor(const char *constructor) const if (priv(d.data)->revision < 2) return -1; for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { - const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i]; + const char *data = rawStringData(this, d.data[priv(d.data)->constructorData + 5*i]); if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { return i; } @@ -618,7 +657,7 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, int conflict = m->d.superdata->indexOfMethod(signal); if (conflict >= 0) qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", - signal, m->d.superdata->d.stringdata, m->d.stringdata); + signal, rawStringData(m->d.superdata, 0), rawStringData(m, 0)); } #endif return i; @@ -654,7 +693,7 @@ int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, const char *name) { while (self) { - if (strcmp(self->d.stringdata, name) == 0) + if (strcmp(rawStringData(self, 0), name) == 0) return self; if (self->d.extradata) { const QMetaObject **e; @@ -690,7 +729,7 @@ int QMetaObject::indexOfEnumerator(const char *name) const while (m) { const QMetaObjectPrivate *d = priv(m->d.data); for (int i = d->enumeratorCount - 1; i >= 0; --i) { - const char *prop = m->d.stringdata + m->d.data[d->enumeratorData + 4*i]; + const char *prop = rawStringData(m, m->d.data[d->enumeratorData + 4*i]); if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { i += m->enumeratorOffset(); return i; @@ -713,7 +752,7 @@ int QMetaObject::indexOfProperty(const char *name) const while (m) { const QMetaObjectPrivate *d = priv(m->d.data); for (int i = d->propertyCount-1; i >= 0; --i) { - const char *prop = m->d.stringdata + m->d.data[d->propertyData + 3*i]; + const char *prop = rawStringData(m, m->d.data[d->propertyData + 3*i]); if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { i += m->propertyOffset(); return i; @@ -744,8 +783,7 @@ int QMetaObject::indexOfClassInfo(const char *name) const const QMetaObject *m = this; while (m && i < 0) { for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i) - if (strcmp(name, m->d.stringdata - + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) { + if (strcmp(name, rawStringData(m, m->d.data[priv(m->d.data)->classInfoData + 2*i])) == 0) { i += m->classInfoOffset(); break; } @@ -829,7 +867,7 @@ QMetaProperty QMetaObject::property(int index) const if (i >= 0 && i < priv(d.data)->propertyCount) { int handle = priv(d.data)->propertyData + 3*i; int flags = d.data[handle + 2]; - const char *type = d.stringdata + d.data[handle + 1]; + const char *type = rawStringData(this, d.data[handle + 1]); result.mobj = this; result.handle = handle; result.idx = i; @@ -838,7 +876,7 @@ QMetaProperty QMetaObject::property(int index) const result.menum = enumerator(indexOfEnumerator(type)); if (!result.menum.isValid()) { const char *enum_name = type; - const char *scope_name = d.stringdata; + const char *scope_name = rawStringData(this, 0); char *scope_buffer = 0; const char *colon = strrchr(enum_name, ':'); @@ -1285,7 +1323,7 @@ const char *QMetaMethod::signature() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -1298,7 +1336,7 @@ QList QMetaMethod::parameterTypes() const if (!mobj) return QList(); return QMetaObjectPrivate::parameterTypeNamesFromSignature( - mobj->d.stringdata + mobj->d.data[handle]); + rawStringData(mobj, mobj->d.data[handle])); } /*! @@ -1311,10 +1349,10 @@ QList QMetaMethod::parameterNames() const QList list; if (!mobj) return list; - const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); if (*names == 0) { // do we have one or zero arguments? - const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + const char *signature = rawStringData(mobj, mobj->d.data[handle]); while (*signature && *signature != '(') ++signature; if (*++signature != ')') @@ -1340,7 +1378,7 @@ const char *QMetaMethod::typeName() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 2]; + return rawStringData(mobj, mobj->d.data[handle + 2]); } /*! @@ -1377,7 +1415,7 @@ const char *QMetaMethod::tag() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 3]; + return rawStringData(mobj, mobj->d.data[handle + 3]); } @@ -1580,10 +1618,10 @@ bool QMetaMethod::invoke(QObject *object, int metaMethodArgumentCount = 0; { // based on QMetaObject::parameterNames() - const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); if (*names == 0) { // do we have one or zero arguments? - const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + const char *signature = rawStringData(mobj, mobj->d.data[handle]); while (*signature && *signature != '(') ++signature; if (*++signature != ')') @@ -1803,7 +1841,7 @@ const char *QMetaEnum::name() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -1831,7 +1869,7 @@ const char *QMetaEnum::key(int index) const int count = mobj->d.data[handle + 2]; int data = mobj->d.data[handle + 3]; if (index >= 0 && index < count) - return mobj->d.stringdata + mobj->d.data[data + 2*index]; + return rawStringData(mobj, mobj->d.data[data + 2*index]); return 0; } @@ -1878,7 +1916,7 @@ bool QMetaEnum::isFlag() const */ const char *QMetaEnum::scope() const { - return mobj?mobj->d.stringdata : 0; + return mobj?rawStringData(mobj, 0) : 0; } /*! @@ -1910,8 +1948,8 @@ int QMetaEnum::keyToValue(const char *key, bool *ok) const int count = mobj->d.data[handle + 2]; int data = mobj->d.data[handle + 3]; for (int i = 0; i < count; ++i) { - if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key, mobj->d.stringdata, scope) == 0)) - && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + if ((!scope || (stringSize(mobj, 0) == int(scope) && strncmp(qualified_key, rawStringData(mobj, 0), scope) == 0)) + && strcmp(key, rawStringData(mobj, mobj->d.data[data + 2*i])) == 0) { if (ok != 0) *ok = true; return mobj->d.data[data + 2*i + 1]; @@ -1936,7 +1974,7 @@ const char* QMetaEnum::valueToKey(int value) const int data = mobj->d.data[handle + 3]; for (int i = 0; i < count; ++i) if (value == (int)mobj->d.data[data + 2*i + 1]) - return mobj->d.stringdata + mobj->d.data[data + 2*i]; + return rawStringData(mobj, mobj->d.data[data + 2*i]); return 0; } @@ -1979,8 +2017,8 @@ int QMetaEnum::keysToValue(const char *keys, bool *ok) const } int i; for (i = count-1; i >= 0; --i) - if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key.constData(), mobj->d.stringdata, scope) == 0)) - && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + if ((!scope || (stringSize(mobj, 0) == int(scope) && strncmp(qualified_key.constData(), rawStringData(mobj, 0), scope) == 0)) + && strcmp(key, rawStringData(mobj, mobj->d.data[data + 2*i])) == 0) { value |= mobj->d.data[data + 2*i + 1]; break; } @@ -2013,7 +2051,7 @@ QByteArray QMetaEnum::valueToKeys(int value) const v = v & ~k; if (!keys.isEmpty()) keys += '|'; - keys += mobj->d.stringdata + mobj->d.data[data + 2*i]; + keys += rawStringData(mobj, mobj->d.data[data + 2*i]); } } return keys; @@ -2092,7 +2130,7 @@ const char *QMetaProperty::name() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -2105,7 +2143,7 @@ const char *QMetaProperty::typeName() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return mobj->d.stringdata + mobj->d.data[handle + 1]; + return rawStringData(mobj, mobj->d.data[handle + 1]); } /*! @@ -2254,7 +2292,7 @@ QVariant QMetaProperty::read(const QObject *object) const } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; uint flags = mobj->d.data[handle + 2]; - const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); t = (flags >> 24); if (t == QVariant::Invalid) t = QMetaType::type(typeName); @@ -2325,7 +2363,7 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const uint flags = mobj->d.data[handle + 2]; t = flags >> 24; if (t == QVariant::Invalid) { - const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); const char *vtypeName = value.typeName(); if (vtypeName && strcmp(typeName, vtypeName) == 0) t = value.userType(); @@ -2691,7 +2729,7 @@ const char *QMetaClassInfo::name() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -2703,7 +2741,7 @@ const char* QMetaClassInfo::value() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 1]; + return rawStringData(mobj, mobj->d.data[handle + 1]); } /*! diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 59a5c5f280..d789711bb4 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -109,7 +109,7 @@ class QMutex; struct QMetaObjectPrivate { - enum { OutputRevision = 6 }; // Used by moc and qmetaobjectbuilder + enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus int revision; int className; @@ -122,10 +122,13 @@ struct QMetaObjectPrivate int signalCount; //since revision 4 // revision 5 introduces changes in normalized signatures, no new members // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself + // revision 7 is Qt 5 static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast(metaobject->d.data); } + static const char *rawStringData(const QMetaObject *mo, int index); + static int indexOfSignalRelative(const QMetaObject **baseObject, const char* name, bool normalizeStringData); diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 8fa5dcdcff..d8c82ddc9f 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4036,9 +4036,9 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa locker.unlock(); // reconstruct the signature to call connectNotify - const char *sig = senderMetaObject->d.stringdata + senderMetaObject->d.data[ + const char *sig = QMetaObjectPrivate::rawStringData(senderMetaObject, senderMetaObject->d.data[ reinterpret_cast(senderMetaObject->d.data)->methodData - + 5 * (signal_index - signalOffset)]; + + 5 * (signal_index - signalOffset)]); QVarLengthArray signalSignature(qstrlen(sig) + 2); signalSignature.data()[0] = char(QSIGNAL_CODE + '0'); strcpy(signalSignature.data() + 1 , sig); diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index ec51251531..6c40733877 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -50,11 +50,12 @@ QT_BEGIN_NAMESPACE class QByteArray; +struct QByteArrayData; class QString; #ifndef Q_MOC_OUTPUT_REVISION -#define Q_MOC_OUTPUT_REVISION 64 +#define Q_MOC_OUTPUT_REVISION 65 #endif // The following macros are our "extensions" to C++ @@ -437,7 +438,7 @@ struct Q_CORE_EXPORT QMetaObject struct { // private data const QMetaObject *superdata; - const char *stringdata; + const QByteArrayData *stringdata; const uint *data; const void *extradata; } d; @@ -478,9 +479,6 @@ struct QMetaObjectExtraData StaticMetacallFunction static_metacall; }; -inline const char *QMetaObject::className() const -{ return d.stringdata; } - inline const QMetaObject *QMetaObject::superClass() const { return d.superdata; } diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index ac602fd6e8..9571af4bd5 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -109,24 +109,17 @@ static inline int lengthOfEscapeSequence(const QByteArray &s, int i) return i - startPos; } -int Generator::strreg(const QByteArray &s) +void Generator::strreg(const QByteArray &s) { - int idx = 0; - for (int i = 0; i < strings.size(); ++i) { - const QByteArray &str = strings.at(i); - if (str == s) - return idx; - idx += str.length() + 1; - for (int i = 0; i < str.length(); ++i) { - if (str.at(i) == '\\') { - int cnt = lengthOfEscapeSequence(str, i) - 1; - idx -= cnt; - i += cnt; - } - } - } - strings.append(s); - return idx; + if (!strings.contains(s)) + strings.append(s); +} + +int Generator::stridx(const QByteArray &s) +{ + int i = strings.indexOf(s); + Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings"); + return i; } void Generator::generateCode() @@ -135,10 +128,6 @@ void Generator::generateCode() bool isQObject = (cdef->classname == "QObject"); bool isConstructible = !cdef->constructorList.isEmpty(); -// -// build the data array -// - // filter out undeclared enumerators and sets { QList enumList; @@ -156,15 +145,119 @@ void Generator::generateCode() cdef->enumList = enumList; } +// +// Register all strings used in data section +// + strreg(cdef->qualified); + registerClassInfoStrings(); + registerFunctionStrings(cdef->signalList); + registerFunctionStrings(cdef->slotList); + registerFunctionStrings(cdef->methodList); + registerFunctionStrings(cdef->constructorList); + registerPropertyStrings(); + registerEnumStrings(); QByteArray qualifiedClassNameIdentifier = cdef->qualified; qualifiedClassNameIdentifier.replace(':', '_'); +// +// Build stringdata struct +// + fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData()); + fprintf(out, " QByteArrayData data[%d];\n", strings.size()); + { + int len = 0; + for (int i = 0; i < strings.size(); ++i) + len += strings.at(i).length() + 1; + fprintf(out, " char stringdata[%d];\n", len + 1); + } + fprintf(out, "};\n"); + + // Macro that expands into a QByteArrayData. The offset member is + // calculated from 1) the offset of the actual characters in the + // stringdata.stringdata member, and 2) the stringdata.data index of the + // QByteArrayData being defined. This calculation relies on the + // QByteArrayData::data() implementation returning simply "this + offset". + fprintf(out, "#define QT_MOC_LITERAL(idx, ofs, len) { \\\n" + " Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \\\n" + " offsetof(qt_meta_stringdata_%s_t, stringdata) + ofs \\\n" + " - idx * sizeof(QByteArrayData) \\\n" + " }\n", + qualifiedClassNameIdentifier.constData()); + + fprintf(out, "static const qt_meta_stringdata_%s_t qt_meta_stringdata_%s = {\n", + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + fprintf(out, " {\n"); + { + int idx = 0; + for (int i = 0; i < strings.size(); ++i) { + if (i) + fprintf(out, ",\n"); + const QByteArray &str = strings.at(i); + fprintf(out, "QT_MOC_LITERAL(%d, %d, %d)", i, idx, str.length()); + idx += str.length() + 1; + for (int j = 0; j < str.length(); ++j) { + if (str.at(j) == '\\') { + int cnt = lengthOfEscapeSequence(str, j) - 1; + idx -= cnt; + j += cnt; + } + } + } + fprintf(out, "\n },\n"); + } + +// +// Build stringdata array +// + fprintf(out, " \""); + int col = 0; + int len = 0; + for (int i = 0; i < strings.size(); ++i) { + QByteArray s = strings.at(i); + len = s.length(); + if (col && col + len >= 72) { + fprintf(out, "\"\n \""); + col = 0; + } else if (len && s.at(0) >= '0' && s.at(0) <= '9') { + fprintf(out, "\"\""); + len += 2; + } + int idx = 0; + while (idx < s.length()) { + if (idx > 0) { + col = 0; + fprintf(out, "\"\n \""); + } + int spanLen = qMin(70, s.length() - idx); + // don't cut escape sequences at the end of a line + int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); + if (backSlashPos >= idx) { + int escapeLen = lengthOfEscapeSequence(s, backSlashPos); + spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx); + } + fwrite(s.constData() + idx, 1, spanLen, out); + idx += spanLen; + col += spanLen; + } + + fputs("\\0", out); + col += len + 2; + } + +// Terminate stringdata struct + fprintf(out, "\"\n};\n"); + fprintf(out, "#undef QT_MOC_LITERAL\n\n"); + +// +// build the data array +// + int index = MetaObjectPrivateFieldCount; fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision)); - fprintf(out, " %4d, // classname\n", strreg(cdef->qualified)); + fprintf(out, " %4d, // classname\n", stridx(cdef->qualified)); fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); index += cdef->classInfoList.count() * 2; @@ -241,46 +334,6 @@ void Generator::generateCode() // fprintf(out, "\n 0 // eod\n};\n\n"); -// -// Build stringdata array -// - fprintf(out, "static const char qt_meta_stringdata_%s[] = {\n", qualifiedClassNameIdentifier.constData()); - fprintf(out, " \""); - int col = 0; - int len = 0; - for (int i = 0; i < strings.size(); ++i) { - QByteArray s = strings.at(i); - len = s.length(); - if (col && col + len >= 72) { - fprintf(out, "\"\n \""); - col = 0; - } else if (len && s.at(0) >= '0' && s.at(0) <= '9') { - fprintf(out, "\"\""); - len += 2; - } - int idx = 0; - while (idx < s.length()) { - if (idx > 0) { - col = 0; - fprintf(out, "\"\n \""); - } - int spanLen = qMin(70, s.length() - idx); - // don't cut escape sequences at the end of a line - int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); - if (backSlashPos >= idx) { - int escapeLen = lengthOfEscapeSequence(s, backSlashPos); - spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx); - } - fwrite(s.constData() + idx, 1, spanLen, out); - idx += spanLen; - col += spanLen; - } - - fputs("\\0", out); - col += len + 2; - } - fprintf(out, "\"\n};\n\n"); - // // Generate internal qt_static_metacall() function // @@ -356,8 +409,9 @@ void Generator::generateCode() fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData()); else fprintf(out, " { 0, "); - fprintf(out, "qt_meta_stringdata_%s,\n qt_meta_data_%s, ", - qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + fprintf(out, "qt_meta_stringdata_%s.data,\n" + " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(), + qualifiedClassNameIdentifier.constData()); if (!hasExtraData) fprintf(out, "0 }\n"); else @@ -379,7 +433,7 @@ void Generator::generateCode() // fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); fprintf(out, " if (!_clname) return 0;\n"); - fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s))\n" + fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata))\n" " return static_cast(const_cast< %s*>(this));\n", qualifiedClassNameIdentifier.constData(), cdef->classname.constData()); for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one @@ -430,6 +484,15 @@ void Generator::generateCode() } +void Generator::registerClassInfoStrings() +{ + for (int i = 0; i < cdef->classInfoList.size(); ++i) { + const ClassInfoDef &c = cdef->classInfoList.at(i); + strreg(c.name); + strreg(c.value); + } +} + void Generator::generateClassInfos() { if (cdef->classInfoList.isEmpty()) @@ -439,7 +502,33 @@ void Generator::generateClassInfos() for (int i = 0; i < cdef->classInfoList.size(); ++i) { const ClassInfoDef &c = cdef->classInfoList.at(i); - fprintf(out, " %4d, %4d,\n", strreg(c.name), strreg(c.value)); + fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value)); + } +} + +void Generator::registerFunctionStrings(const QList& list) +{ + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + + QByteArray sig = f.name + '('; + QByteArray arguments; + + for (int j = 0; j < f.arguments.count(); ++j) { + const ArgumentDef &a = f.arguments.at(j); + if (j) { + sig += ","; + arguments += ","; + } + sig += a.normalizedType; + arguments += a.name; + } + sig += ')'; + + strreg(sig); + strreg(arguments); + strreg(f.normalizedType); + strreg(f.tag); } } @@ -487,8 +576,8 @@ void Generator::generateFunctions(const QList& list, const char *fu flags |= MethodScriptable; if (f.revision > 0) flags |= MethodRevisioned; - fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", strreg(sig), - strreg(arguments), strreg(f.normalizedType), strreg(f.tag), flags); + fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", stridx(sig), + stridx(arguments), stridx(f.normalizedType), stridx(f.tag), flags); } } @@ -502,6 +591,15 @@ void Generator::generateFunctionRevisions(const QList& list, const } } +void Generator::registerPropertyStrings() +{ + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + strreg(p.name); + strreg(p.type); + } +} + void Generator::generateProperties() { // @@ -568,8 +666,8 @@ void Generator::generateProperties() flags |= Final; fprintf(out, " %4d, %4d, ", - strreg(p.name), - strreg(p.type)); + stridx(p.name), + stridx(p.type)); if (!(flags >> 24) && isQRealType(p.type)) fprintf(out, "(QMetaType::QReal << 24) | "); fprintf(out, "0x%.8x,\n", flags); @@ -596,6 +694,16 @@ void Generator::generateProperties() } } +void Generator::registerEnumStrings() +{ + for (int i = 0; i < cdef->enumList.count(); ++i) { + const EnumDef &e = cdef->enumList.at(i); + strreg(e.name); + for (int j = 0; j < e.values.count(); ++j) + strreg(e.values.at(j)); + } +} + void Generator::generateEnums(int index) { if (cdef->enumDeclarations.isEmpty()) @@ -607,7 +715,7 @@ void Generator::generateEnums(int index) for (i = 0; i < cdef->enumList.count(); ++i) { const EnumDef &e = cdef->enumList.at(i); fprintf(out, " %4d, 0x%.1x, %4d, %4d,\n", - strreg(e.name), + stridx(e.name), cdef->enumDeclarations.value(e.name) ? 1 : 0, e.values.count(), index); @@ -624,7 +732,7 @@ void Generator::generateEnums(int index) code += "::" + e.name; code += "::" + val; fprintf(out, " %4d, uint(%s),\n", - strreg(val), code.constData()); + stridx(val), code.constData()); } } } diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 46eee4ca06..c5692f25ae 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -55,17 +55,22 @@ public: Generator(ClassDef *classDef, const QList &metaTypes, FILE *outfile = 0); void generateCode(); private: + void registerClassInfoStrings(); void generateClassInfos(); + void registerFunctionStrings(const QList &list); void generateFunctions(const QList &list, const char *functype, int type); void generateFunctionRevisions(const QList& list, const char *functype); + void registerEnumStrings(); void generateEnums(int index); + void registerPropertyStrings(); void generateProperties(); void generateMetacall(); void generateStaticMetacall(); void generateSignal(FunctionDef *def, int index); void generatePluginMetaData(); - int strreg(const QByteArray &); // registers a string and returns its id + void strreg(const QByteArray &); // registers a string + int stridx(const QByteArray &); // returns a string's id QList strings; QByteArray purestSuperClass; QList metaTypes; diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 7b358c1ae8..385390d954 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -818,6 +818,7 @@ void Moc::generate(FILE *out) if (classList.size() && classList.first().classname == "Qt") fprintf(out, "#include \n"); + fprintf(out, "#include \n"); // For QByteArrayData if (mustIncludeQMetaTypeH) fprintf(out, "#include \n"); if (mustIncludeQPluginH) diff --git a/src/tools/moc/outputrevision.h b/src/tools/moc/outputrevision.h index 2ce5b9b765..cff0f98fca 100644 --- a/src/tools/moc/outputrevision.h +++ b/src/tools/moc/outputrevision.h @@ -43,6 +43,6 @@ #define OUTPUTREVISION_H // if the output revision changes, you MUST change it in qobjectdefs.h too -enum { mocOutputRevision = 64 }; // moc format output revision +enum { mocOutputRevision = 65 }; // moc format output revision #endif // OUTPUTREVISION_H diff --git a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp index 2d180b88ea..021079a8e7 100644 --- a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp +++ b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp @@ -90,7 +90,7 @@ static const char qt_meta_stringdata_OldNormalizeObject[] = { }; const QMetaObject OldNormalizeObject::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_OldNormalizeObject, + { &QObject::staticMetaObject, reinterpret_cast(qt_meta_stringdata_OldNormalizeObject), qt_meta_data_OldNormalizeObject, 0 } }; From 96f2365cf4cebc074c3171878dcd25ce19ee7486 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sat, 18 Feb 2012 23:16:24 +0100 Subject: [PATCH 59/74] Rename QMetaMethod::signature() to methodSignature() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Qt5 the meta-data format will be changed to not store the method signature string explicitly; the signature will be reconstructed on demand from the method name and parameter type information. The QMetaMethod::signature() method returns a const char pointer. Changing the return type to QByteArray can lead to silent bugs due to the implicit conversion to char *. Even though it's a source- incompatible change, it's therefore better to introduce a new function, methodSignature(), and remove the old signature(). Task-number: QTBUG-24154 Change-Id: Ib3579dedd27a3c7c8914d5f1b231947be2cf4027 Reviewed-by: Olivier Goffart Reviewed-by: Lars Knoll Reviewed-by: Thiago Macieira Reviewed-by: João Abecasis --- dist/changes-5.0.0 | 5 ++ .../code/src_corelib_kernel_qmetaobject.cpp | 2 +- src/corelib/kernel/qmetaobject.cpp | 19 ++++--- src/corelib/kernel/qmetaobject.h | 9 +++- src/corelib/kernel/qmetaobjectbuilder.cpp | 12 ++--- src/corelib/kernel/qobject.cpp | 51 ++++++++++--------- src/corelib/statemachine/qstatemachine.cpp | 2 +- src/dbus/qdbusabstractadaptor.cpp | 4 +- src/dbus/qdbusabstractinterface.cpp | 2 +- src/dbus/qdbusintegrator.cpp | 4 +- src/dbus/qdbusmisc.cpp | 8 +-- src/dbus/qdbusxmlgenerator.cpp | 2 +- src/gui/accessible/qaccessibleobject.cpp | 4 +- src/testlib/qsignaldumper.cpp | 4 +- src/testlib/qtestcase.cpp | 11 ++-- .../kernel/qmetamethod/tst_qmetamethod.cpp | 2 +- .../kernel/qmetaobject/tst_qmetaobject.cpp | 10 ++-- .../tst_qmetaobjectbuilder.cpp | 6 +-- .../corelib/kernel/qobject/tst_qobject.cpp | 16 +++--- .../qdbusmetaobject/tst_qdbusmetaobject.cpp | 2 +- tests/auto/tools/moc/tst_moc.cpp | 26 +++++----- .../widgets/widgets/qmdiarea/tst_qmdiarea.cpp | 2 +- .../corelib/kernel/qmetaobject/main.cpp | 6 +-- 23 files changed, 115 insertions(+), 94 deletions(-) diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index bac26e69cd..cb1f8f4528 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -56,6 +56,11 @@ information about a particular change. * QMetaType::construct() has been renamed to QMetaType::create(). * QMetaType::unregisterType() has been removed. +- QMetaMethod::signature() has been renamed to QMetaMethod::methodSignature(), + and the return type has been changed to QByteArray. This was done to be able + to generate the signature string on demand, rather than always storing it in + the meta-data. + - QTestLib: * The plain-text, xml and lightxml test output formats have been changed to show a test result for every row of test data in data-driven tests. In diff --git a/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp b/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp index afba3b6a61..7c0c2c2122 100644 --- a/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp +++ b/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp @@ -107,7 +107,7 @@ for(int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) const QMetaObject* metaObject = obj->metaObject(); QStringList methods; for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) - methods << QString::fromLatin1(metaObject->method(i).signature()); + methods << QString::fromLatin1(metaObject->method(i).methodSignature()); //! [methodCount] //! [6] diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index ce30e34f73..4d629dd933 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -157,6 +157,11 @@ static inline const QByteArrayData &stringData(const QMetaObject *mo, int index) return data; } +static inline QByteArray toByteArray(const QByteArrayData &d) +{ + return QByteArray(reinterpret_cast &>(d)); +} + static inline const char *legacyString(const QMetaObject *mo, int index) { Q_ASSERT(priv(mo->d.data)->revision <= 6); @@ -1268,7 +1273,7 @@ bool QMetaObject::invokeMethod(QObject *obj, \ingroup objectmodel - A QMetaMethod has a methodType(), a signature(), a list of + A QMetaMethod has a methodType(), a methodSignature(), a list of parameterTypes() and parameterNames(), a return typeName(), a tag(), and an access() specifier. You can use invoke() to invoke the method on an arbitrary QObject. @@ -1314,22 +1319,24 @@ bool QMetaObject::invokeMethod(QObject *obj, */ /*! + \since 5.0 + Returns the signature of this method (e.g., \c{setValue(double)}). \sa parameterTypes(), parameterNames() */ -const char *QMetaMethod::signature() const +QByteArray QMetaMethod::methodSignature() const { if (!mobj) - return 0; - return rawStringData(mobj, mobj->d.data[handle]); + return QByteArray(); + return toByteArray(stringData(mobj, mobj->d.data[handle])); } /*! Returns a list of parameter types. - \sa parameterNames(), signature() + \sa parameterNames(), methodSignature() */ QList QMetaMethod::parameterTypes() const { @@ -1342,7 +1349,7 @@ QList QMetaMethod::parameterTypes() const /*! Returns a list of parameter names. - \sa parameterTypes(), signature() + \sa parameterTypes(), methodSignature() */ QList QMetaMethod::parameterNames() const { diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 9e51af7556..573e69fdb7 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -57,7 +57,7 @@ class Q_CORE_EXPORT QMetaMethod public: inline QMetaMethod() : mobj(0),handle(0) {} - const char *signature() const; + QByteArray methodSignature() const; const char *typeName() const; QList parameterTypes() const; QList parameterNames() const; @@ -137,6 +137,13 @@ public: inline bool isValid() const { return mobj != 0; } private: +#if QT_DEPRECATED_SINCE(5,0) + // signature() has been renamed to methodSignature() in Qt 5. + // Warning, that function returns a QByteArray; check the life time if + // you convert to char*. + char *signature(struct renamedInQt5_warning_checkTheLifeTime * = 0) Q_DECL_EQ_DELETE; +#endif + const QMetaObject *mobj; uint handle; friend struct QMetaObject; diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 8bece6636b..82c74a34f3 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -458,13 +458,13 @@ QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod& prototype) { QMetaMethodBuilder method; if (prototype.methodType() == QMetaMethod::Method) - method = addMethod(prototype.signature()); + method = addMethod(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Signal) - method = addSignal(prototype.signature()); + method = addSignal(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Slot) - method = addSlot(prototype.signature()); + method = addSlot(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Constructor) - method = addConstructor(prototype.signature()); + method = addConstructor(prototype.methodSignature()); method.setReturnType(prototype.typeName()); method.setParameterNames(prototype.parameterNames()); method.setTag(prototype.tag()); @@ -535,7 +535,7 @@ QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray& signatur QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod& prototype) { Q_ASSERT(prototype.methodType() == QMetaMethod::Constructor); - QMetaMethodBuilder ctor = addConstructor(prototype.signature()); + QMetaMethodBuilder ctor = addConstructor(prototype.methodSignature()); ctor.setReturnType(prototype.typeName()); ctor.setParameterNames(prototype.parameterNames()); ctor.setTag(prototype.tag()); @@ -588,7 +588,7 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& protot if (prototype.hasNotifySignal()) { // Find an existing method for the notify signal, or add a new one. QMetaMethod method = prototype.notifySignal(); - int index = indexOfMethod(method.signature()); + int index = indexOfMethod(method.methodSignature()); if (index == -1) index = addMethod(method).index(); d->properties[property._index].notifySignal = index; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index d8c82ddc9f..f44e4c4761 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2174,12 +2174,12 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM if (signal.attributes() & QMetaMethod::Compatibility) { if (!(method.attributes() & QMetaMethod::Compatibility)) qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", - sender->className(), signal.signature()); + sender->className(), signal.methodSignature().constData()); } else if ((method.attributes() & QMetaMethod::Compatibility) && method.methodType() == QMetaMethod::Signal) { qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", - sender->className(), signal.signature(), - receiver->className(), method.signature()); + sender->className(), signal.methodSignature().constData(), + receiver->className(), method.methodSignature().constData()); } } @@ -2419,17 +2419,17 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho || method.methodType() == QMetaMethod::Constructor) { qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", sender ? sender->metaObject()->className() : "(null)", - signal.signature(), + signal.methodSignature().constData(), receiver ? receiver->metaObject()->className() : "(null)", - method.signature() ); + method.methodSignature().constData() ); return QMetaObject::Connection(0); } - // Reconstructing SIGNAL() macro result for signal.signature() string + // Reconstructing SIGNAL() macro result for signal.methodSignature() string QByteArray signalSignature; - signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.reserve(signal.methodSignature().size()+1); signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); + signalSignature.append(signal.methodSignature()); int signal_index; int method_index; @@ -2443,20 +2443,20 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho const QMetaObject *rmeta = receiver->metaObject(); if (signal_index == -1) { qWarning("QObject::connect: Can't find signal %s on instance of class %s", - signal.signature(), smeta->className()); + signal.methodSignature().constData(), smeta->className()); return QMetaObject::Connection(0); } if (method_index == -1) { qWarning("QObject::connect: Can't find method %s on instance of class %s", - method.signature(), rmeta->className()); + method.methodSignature().constData(), rmeta->className()); return QMetaObject::Connection(0); } - if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { + if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(), method.methodSignature().constData())) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", - smeta->className(), signal.signature(), - rmeta->className(), method.signature()); + smeta->className(), signal.methodSignature().constData(), + rmeta->className(), method.methodSignature().constData()); return QMetaObject::Connection(0); } @@ -2688,24 +2688,24 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, if(signal.methodType() != QMetaMethod::Signal) { qWarning("QObject::%s: Attempt to %s non-signal %s::%s", "disconnect","unbind", - sender->metaObject()->className(), signal.signature()); + sender->metaObject()->className(), signal.methodSignature().constData()); return false; } } if (method.mobj) { if(method.methodType() == QMetaMethod::Constructor) { qWarning("QObject::disconect: cannot use constructor as argument %s::%s", - receiver->metaObject()->className(), method.signature()); + receiver->metaObject()->className(), method.methodSignature().constData()); return false; } } - // Reconstructing SIGNAL() macro result for signal.signature() string + // Reconstructing SIGNAL() macro result for signal.methodSignature() string QByteArray signalSignature; if (signal.mobj) { - signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.reserve(signal.methodSignature().size()+1); signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); + signalSignature.append(signal.methodSignature()); } int signal_index; @@ -2719,13 +2719,13 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, // is -1 then this signal is not a member of sender. if (signal.mobj && signal_index == -1) { qWarning("QObject::disconect: signal %s not found on class %s", - signal.signature(), sender->metaObject()->className()); + signal.methodSignature().constData(), sender->metaObject()->className()); return false; } // If this condition is true then method is not a member of receeiver. if (receiver && method.mobj && method_index == -1) { qWarning("QObject::disconect: method %s not found on class %s", - method.signature(), receiver->metaObject()->className()); + method.methodSignature().constData(), receiver->metaObject()->className()); return false; } @@ -3040,7 +3040,8 @@ void QMetaObject::connectSlotsByName(QObject *o) Q_ASSERT(mo); const QObjectList list = o->findChildren(QString()); for (int i = 0; i < mo->methodCount(); ++i) { - const char *slot = mo->method(i).signature(); + QByteArray slotSignature = mo->method(i).methodSignature(); + const char *slot = slotSignature.constData(); Q_ASSERT(slot); if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_') continue; @@ -3060,7 +3061,7 @@ void QMetaObject::connectSlotsByName(QObject *o) if (method.methodType() != QMetaMethod::Signal) continue; - if (!qstrncmp(method.signature(), slot + len + 4, slotlen)) { + if (!qstrncmp(method.methodSignature().constData(), slot + len + 4, slotlen)) { int signalOffset, methodOffset; computeOffsets(method.enclosingMetaObject(), &signalOffset, &methodOffset); sigIndex = k + - methodOffset + signalOffset; @@ -3531,7 +3532,7 @@ void QObject::dumpObjectInfo() offset = methodOffset - signalOffset; } const QMetaMethod signal = metaObject()->method(signal_index + offset); - qDebug(" signal: %s", signal.signature()); + qDebug(" signal: %s", signal.methodSignature().constData()); // receivers const QObjectPrivate::Connection *c = @@ -3547,7 +3548,7 @@ void QObject::dumpObjectInfo() qDebug(" --> %s::%s %s", receiverMetaObject->className(), c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()), - method.signature()); + method.methodSignature().constData()); c = c->nextConnectionList; } } @@ -3564,7 +3565,7 @@ void QObject::dumpObjectInfo() qDebug(" <-- %s::%s %s", s->sender->metaObject()->className(), s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), - slot.signature()); + slot.methodSignature().constData()); } } else { qDebug(" "); diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 7ff005f9a1..3992c4060e 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1657,7 +1657,7 @@ void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalInd #ifdef QSTATEMACHINE_DEBUG qDebug() << q_func() << ": sending signal event ( sender =" << sender - << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')'; + << ", signal =" << sender->metaObject()->method(signalIndex).methodSignature().constData() << ')'; #endif postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs)); processEvents(DirectProcessing); diff --git a/src/dbus/qdbusabstractadaptor.cpp b/src/dbus/qdbusabstractadaptor.cpp index 3ba8acca82..bacf93bac8 100644 --- a/src/dbus/qdbusabstractadaptor.cpp +++ b/src/dbus/qdbusabstractadaptor.cpp @@ -181,7 +181,7 @@ void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) continue; // try to connect/disconnect to a signal on the parent that has the same method signature - QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); + QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData()); if (them->indexOfSignal(sig) == -1) continue; sig.prepend(QSIGNAL_CODE + '0'); @@ -307,7 +307,7 @@ void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void ** // invalid signal signature // qDBusParametersForMethod has not yet complained about this one qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s", - senderMetaObject->className(), mm.signature()); + senderMetaObject->className(), mm.methodSignature().constData()); return; } diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp index eeaf0b13eb..cb9f2e7360 100644 --- a/src/dbus/qdbusabstractinterface.cpp +++ b/src/dbus/qdbusabstractinterface.cpp @@ -446,7 +446,7 @@ QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode, for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { QMetaMethod mm = mo->method(i); - if (QByteArray(mm.signature()).startsWith(match)) { + if (mm.methodSignature().startsWith(match)) { // found a method with the same name as what we're looking for // hopefully, nobody is overloading asynchronous and synchronous methods with // the same name diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index f0c8224be2..f86365025f 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -640,7 +640,7 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, continue; // check name: - QByteArray slotname = mm.signature(); + QByteArray slotname = mm.methodSignature(); int paren = slotname.indexOf('('); if (paren != name.length() || !slotname.startsWith(name)) continue; @@ -1188,7 +1188,7 @@ void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, in QString interface = qDBusInterfaceFromMetaObject(mo); QMetaMethod mm = mo->method(signalId); - QByteArray memberName = mm.signature(); + QByteArray memberName = mm.methodSignature(); memberName.truncate(memberName.indexOf('(')); // check if it's scriptable diff --git a/src/dbus/qdbusmisc.cpp b/src/dbus/qdbusmisc.cpp index 7d68bf1185..0a5da604f2 100644 --- a/src/dbus/qdbusmisc.cpp +++ b/src/dbus/qdbusmisc.cpp @@ -141,7 +141,7 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList& metaTypes) for ( ; it != end; ++it) { const QByteArray &type = *it; if (type.endsWith('*')) { - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // pointer? return -1; } @@ -152,7 +152,7 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList& metaTypes) int id = QMetaType::type(basictype); if (id == 0) { - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // invalid type in method parameter list return -1; } else if (QDBusMetaType::typeToSignature(id) == 0) @@ -164,14 +164,14 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList& metaTypes) } if (seenMessage) { // && !type.endsWith('&') - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // non-output parameters after message or after output params return -1; // not allowed } int id = QMetaType::type(type); if (id == 0) { - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // invalid type in method parameter list return -1; } diff --git a/src/dbus/qdbusxmlgenerator.cpp b/src/dbus/qdbusxmlgenerator.cpp index a6572b2c86..550c82a0f2 100644 --- a/src/dbus/qdbusxmlgenerator.cpp +++ b/src/dbus/qdbusxmlgenerator.cpp @@ -126,7 +126,7 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method // now add methods: for (int i = methodOffset; i < mo->methodCount(); ++i) { QMetaMethod mm = mo->method(i); - QByteArray signature = mm.signature(); + QByteArray signature = mm.methodSignature(); int paren = signature.indexOf('('); bool isSignal; diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index e587ad077c..1f4e005dfe 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -78,10 +78,10 @@ QList QAccessibleObjectPrivate::actionList() const continue; if (!qstrcmp(member.tag(), "QACCESSIBLE_SLOT")) { - if (member.signature() == defaultAction) + if (member.methodSignature() == defaultAction) actionList.prepend(defaultAction); else - actionList << member.signature(); + actionList << member.methodSignature(); } } diff --git a/src/testlib/qsignaldumper.cpp b/src/testlib/qsignaldumper.cpp index 4fd870b644..e15add0c8d 100644 --- a/src/testlib/qsignaldumper.cpp +++ b/src/testlib/qsignaldumper.cpp @@ -66,7 +66,7 @@ enum { IndentSpacesCount = 4 }; static QByteArray memberName(const QMetaMethod &member) { - QByteArray ba = member.signature(); + QByteArray ba = member.methodSignature(); return ba.left(ba.indexOf('(')); } @@ -152,7 +152,7 @@ static void qSignalDumperCallbackSlot(QObject *caller, int method_index, void ** str += QByteArray::number(quintptr(caller), 16); str += ") "; - str += member.signature(); + str += member.methodSignature(); qPrintMessage(str); } diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index a4f1a39bbd..d02f449d70 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1061,7 +1061,8 @@ static bool isValidSlot(const QMetaMethod &sl) if (sl.access() != QMetaMethod::Private || !sl.parameterTypes().isEmpty() || qstrlen(sl.typeName()) || sl.methodType() != QMetaMethod::Slot) return false; - const char *sig = sl.signature(); + QByteArray signature = sl.methodSignature(); + const char *sig = signature.constData(); int len = qstrlen(sig); if (len < 2) return false; @@ -1084,7 +1085,7 @@ static void qPrintTestSlots(FILE *stream) for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) { QMetaMethod sl = QTest::currentTestObject->metaObject()->method(i); if (isValidSlot(sl)) - fprintf(stream, "%s\n", sl.signature()); + fprintf(stream, "%s\n", sl.methodSignature().constData()); } } @@ -1109,7 +1110,7 @@ static void qPrintDataTags(FILE *stream) // Retrieve local tags: QStringList localTags; QTestTable table; - char *slot = qstrdup(tf.signature()); + char *slot = qstrdup(tf.methodSignature().constData()); slot[strlen(slot) - 2] = '\0'; QByteArray member; member.resize(qstrlen(slot) + qstrlen("_data()") + 1); @@ -1779,7 +1780,7 @@ static void qInvokeTestMethods(QObject *testObject) if (QTest::testFuncs) { for (int i = 0; i != QTest::testFuncCount; i++) { - if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).signature(), + if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).methodSignature().constData(), QTest::testFuncs[i].data())) { break; } @@ -1793,7 +1794,7 @@ static void qInvokeTestMethods(QObject *testObject) for (int i = 0; i != methodCount; i++) { if (!isValidSlot(testMethods[i])) continue; - if (!qInvokeTestMethod(testMethods[i].signature())) + if (!qInvokeTestMethod(testMethods[i].methodSignature().constData())) break; } delete[] testMethods; diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index 1651d00738..f653e66bfd 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -603,7 +603,7 @@ void tst_QMetaMethod::method() QCOMPARE(method.methodType(), methodType); QCOMPARE(method.access(), access); - QCOMPARE(method.signature(), signature.constData()); + QCOMPARE(method.methodSignature(), signature); QCOMPARE(method.tag(), ""); diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 09fd0a7adb..b6b68338cd 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -978,25 +978,25 @@ void tst_QMetaObject::propertyNotify() QVERIFY(prop.isValid()); QVERIFY(prop.hasNotifySignal()); QMetaMethod signal = prop.notifySignal(); - QCOMPARE(signal.signature(), "value6Changed()"); + QCOMPARE(signal.methodSignature(), QByteArray("value6Changed()")); prop = mo->property(mo->indexOfProperty("value7")); QVERIFY(prop.isValid()); QVERIFY(prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), "value7Changed(QString)"); + QCOMPARE(signal.methodSignature(), QByteArray("value7Changed(QString)")); prop = mo->property(mo->indexOfProperty("value8")); QVERIFY(prop.isValid()); QVERIFY(!prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), (const char *)0); + QCOMPARE(signal.methodSignature(), QByteArray()); prop = mo->property(mo->indexOfProperty("value")); QVERIFY(prop.isValid()); QVERIFY(!prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), (const char *)0); + QCOMPARE(signal.methodSignature(), QByteArray()); } void tst_QMetaObject::propertyConstant() @@ -1114,7 +1114,7 @@ void tst_QMetaObject::indexOfMethod() QFETCH(bool, isSignal); int idx = object->metaObject()->indexOfMethod(name); QVERIFY(idx >= 0); - QCOMPARE(object->metaObject()->method(idx).signature(), name.constData()); + QCOMPARE(object->metaObject()->method(idx).methodSignature(), name); QCOMPARE(object->metaObject()->indexOfSlot(name), isSignal ? -1 : idx); QCOMPARE(object->metaObject()->indexOfSignal(name), !isSignal ? -1 : idx); } diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 97b14a374e..28794050c9 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -1161,7 +1161,7 @@ bool tst_QMetaObjectBuilder::checkForSideEffects static bool sameMethod(const QMetaMethod& method1, const QMetaMethod& method2) { - if (QByteArray(method1.signature()) != QByteArray(method2.signature())) + if (method1.methodSignature() != method2.methodSignature()) return false; if (QByteArray(method1.typeName()) != QByteArray(method2.typeName())) @@ -1466,7 +1466,7 @@ void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break; default: { QMetaMethod ctor = _o->metaObject()->constructor(_id); - qFatal("You forgot to add a case for CreateInstance %s", ctor.signature()); + qFatal("You forgot to add a case for CreateInstance %s", ctor.methodSignature().constData()); } } } else if (_c == QMetaObject::InvokeMetaMethod) { @@ -1478,7 +1478,7 @@ void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, case 2: *reinterpret_cast(_a[0]) = _t->listInvokableQRealQString(*reinterpret_cast(_a[1]), *reinterpret_cast(_a[2])); break; default: { QMetaMethod method = _o->metaObject()->method(_o->metaObject()->methodOffset() + _id); - qFatal("You forgot to add a case for InvokeMetaMethod %s", method.signature()); + qFatal("You forgot to add a case for InvokeMetaMethod %s", method.methodSignature().constData()); } } } else if (_c == QMetaObject::IndexOfMethod) { diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index a6ad1d53bc..c6667ff2a8 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -1793,56 +1793,56 @@ void tst_QObject::metamethod() QMetaMethod m; m = mobj->method(mobj->indexOfMethod("invoke1()")); - QVERIFY(QByteArray(m.signature()) == "invoke1()"); + QVERIFY(m.methodSignature() == "invoke1()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke1()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke1()"); + QVERIFY(m.methodSignature() == "sinvoke1()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("invoke2()")); - QVERIFY(QByteArray(m.signature()) == "invoke2()"); + QVERIFY(m.methodSignature() == "invoke2()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke2()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke2()"); + QVERIFY(m.methodSignature() == "sinvoke2()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("invoke3()")); - QVERIFY(QByteArray(m.signature()) == "invoke3()"); + QVERIFY(m.methodSignature() == "invoke3()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Private); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke3()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke3()"); + QVERIFY(m.methodSignature() == "sinvoke3()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Private); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("signal5()")); - QVERIFY(QByteArray(m.signature()) == "signal5()"); + QVERIFY(m.methodSignature() == "signal5()"); QVERIFY(m.methodType() == QMetaMethod::Signal); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("aPublicSlot()")); - QVERIFY(QByteArray(m.signature()) == "aPublicSlot()"); + QVERIFY(m.methodSignature() == "aPublicSlot()"); QVERIFY(m.methodType() == QMetaMethod::Slot); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); diff --git a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp index ed4ed4c6a2..f705fe474c 100644 --- a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp +++ b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp @@ -397,7 +397,7 @@ void tst_QDBusMetaObject::types() for (int i = metaobject->methodOffset(); i < metaobject->methodCount(); ++i) { QMetaMethod expected = metaobject->method(i); - int methodIdx = result->indexOfMethod(expected.signature()); + int methodIdx = result->indexOfMethod(expected.methodSignature().constData()); QVERIFY(methodIdx != -1); QMetaMethod constructed = result->method(methodIdx); diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 27db6cc59c..ae3780207b 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -747,7 +747,7 @@ void tst_Moc::classinfoWithEscapes() QCOMPARE(mobj->methodCount() - mobj->methodOffset(), 1); QMetaMethod mm = mobj->method(mobj->methodOffset()); - QCOMPARE(mm.signature(), "slotWithAReallyLongName(int)"); + QCOMPARE(mm.methodSignature(), QByteArray("slotWithAReallyLongName(int)")); } void tst_Moc::trNoopInClassInfo() @@ -1092,14 +1092,14 @@ void tst_Moc::invokable() { const QMetaObject &mobj = InvokableBeforeReturnType::staticMetaObject; QCOMPARE(mobj.methodCount(), 6); - QVERIFY(mobj.method(5).signature() == QByteArray("foo()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("foo()")); } { const QMetaObject &mobj = InvokableBeforeInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("foo()")); - QVERIFY(mobj.method(6).signature() == QByteArray("bar()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("foo()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("bar()")); } } @@ -1108,22 +1108,22 @@ void tst_Moc::singleFunctionKeywordSignalAndSlot() { const QMetaObject &mobj = SingleFunctionKeywordBeforeReturnType::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("mySignal()")); - QVERIFY(mobj.method(6).signature() == QByteArray("mySlot()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("mySignal()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("mySlot()")); } { const QMetaObject &mobj = SingleFunctionKeywordBeforeInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("mySignal()")); - QVERIFY(mobj.method(6).signature() == QByteArray("mySlot()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("mySignal()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("mySlot()")); } { const QMetaObject &mobj = SingleFunctionKeywordAfterInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("mySignal()")); - QVERIFY(mobj.method(6).signature() == QByteArray("mySlot()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("mySignal()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("mySlot()")); } } @@ -1230,7 +1230,7 @@ void tst_Moc::constructors() QMetaMethod mm = mo->constructor(0); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); - QCOMPARE(mm.signature(), "CtorTestClass(QObject*)"); + QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass(QObject*)")); QCOMPARE(mm.typeName(), ""); QList paramNames = mm.parameterNames(); QCOMPARE(paramNames.size(), 1); @@ -1243,7 +1243,7 @@ void tst_Moc::constructors() QMetaMethod mm = mo->constructor(1); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); - QCOMPARE(mm.signature(), "CtorTestClass()"); + QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass()")); QCOMPARE(mm.typeName(), ""); QCOMPARE(mm.parameterNames().size(), 0); QCOMPARE(mm.parameterTypes().size(), 0); @@ -1252,7 +1252,7 @@ void tst_Moc::constructors() QMetaMethod mm = mo->constructor(2); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); - QCOMPARE(mm.signature(), "CtorTestClass(QString)"); + QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass(QString)")); QCOMPARE(mm.typeName(), ""); QList paramNames = mm.parameterNames(); QCOMPARE(paramNames.size(), 1); diff --git a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp index 82632a018c..e97b044dbc 100644 --- a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp +++ b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp @@ -1275,7 +1275,7 @@ static int numberOfConnectedSignals(MySubWindow *subWindow) QMetaMethod method = subWindow->metaObject()->method(i); if (method.methodType() == QMetaMethod::Signal) { QString signature(QLatin1String("2")); - signature += QLatin1String(method.signature()); + signature += QLatin1String(method.methodSignature().constData()); numConnectedSignals += subWindow->receivers(signature.toLatin1()); } } diff --git a/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp b/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp index ab26158f9a..6d7c5c3853 100644 --- a/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp +++ b/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp @@ -174,7 +174,7 @@ void tst_qmetaobject::indexOfMethod_data() const QMetaObject *mo = &QTreeView::staticMetaObject; for (int i = 0; i < mo->methodCount(); ++i) { QMetaMethod method = mo->method(i); - QByteArray sig = method.signature(); + QByteArray sig = method.methodSignature(); QTest::newRow(sig) << sig; } } @@ -197,7 +197,7 @@ void tst_qmetaobject::indexOfSignal_data() QMetaMethod method = mo->method(i); if (method.methodType() != QMetaMethod::Signal) continue; - QByteArray sig = method.signature(); + QByteArray sig = method.methodSignature(); QTest::newRow(sig) << sig; } } @@ -220,7 +220,7 @@ void tst_qmetaobject::indexOfSlot_data() QMetaMethod method = mo->method(i); if (method.methodType() != QMetaMethod::Slot) continue; - QByteArray sig = method.signature(); + QByteArray sig = method.methodSignature(); QTest::newRow(sig) << sig; } } From f95181c7bb340744a0ce172e8c5a8fcdc2543297 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sun, 19 Feb 2012 00:15:00 +0100 Subject: [PATCH 60/74] Long live Qt5 meta-object method/property descriptors This commit introduces two significant changes to the meta-object data format: 1) Meta-type information (QMetaType type/name) information is stored directly in the meta-data for both properties and methods; 2) The original signature (string) of a method is no longer stored in the meta-data, since it can be reconstructed from the method name and parameter type info. The motivation for this change is to enable direct access to method names and type information (avoiding string-based lookup for types if possible), since that's typically the information language bindings (e.g. QML) need. (moc already had all the desired information about methods, but it threw it away!) This change keeps support for the older (6 and below) meta-object revisions, but the support will be removed after a short grace period. The following public QMetaMethod functions have been added: name() : QByteArray returnType() : int parameterCount() : int parameterType(int index) : int The following internal QMetaMethod function has been added: getParameterTypes(int *types) : void This commit extends the meta-method data to include explicit type/name data for methods. The new data follows the existing (5-word) method descriptors in the meta-data. The method descriptor format was modified to enable this. First, the descriptor now contains the meta-data index where the method's type/name information can be found. Second, the descriptor contains the number of parameters. Third, the descriptor has a reference to the name of the method, not the full signature. Each entry of a method's type/name array contains either the type id (if it could be determined at meta-object definition time), or a reference to the name of the type (so that the type id can be resolved at runtime). Lastly, instead of storing the method parameter names as a comma-separated list that needs to be parsed at runtime (which was how it was done prior to this commit), the names are now stored as separate entries in the meta-object string table, and their indexes are stored immediately after the method type info array. Hence, parameter names can be queried through the public API without parsing/allocating/copying, too. Task-number: QTBUG-24154 Change-Id: Idb7ab81f12d4bfd658b74e18a0fce594f580cba3 Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/kernel/qmetaobject.cpp | 706 ++++++++++++++++-- src/corelib/kernel/qmetaobject.h | 6 + src/corelib/kernel/qmetaobject_p.h | 74 ++ src/corelib/kernel/qobject.cpp | 211 ++++-- src/corelib/kernel/qobjectdefs.h | 2 + src/tools/moc/generator.cpp | 183 +++-- src/tools/moc/generator.h | 3 +- src/tools/moc/moc.cpp | 5 +- src/tools/moc/moc.h | 3 +- .../kernel/qmetamethod/tst_qmetamethod.cpp | 44 +- 10 files changed, 1065 insertions(+), 172 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 4d629dd933..0a1fb3daf7 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -189,6 +189,53 @@ static inline int stringSize(const QMetaObject *mo, int index) return qstrlen(legacyString(mo, index)); } +static inline QByteArray typeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + if (typeInfo & IsUnresolvedType) { + return toByteArray(stringData(mo, typeInfo & TypeNameIndexMask)); + } else { + // ### Use the QMetaType::typeName() that returns QByteArray + const char *t = QMetaType::typeName(typeInfo); + return QByteArray::fromRawData(t, qstrlen(t)); + } +} + +static inline const char *rawTypeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + return typeNameFromTypeInfo(mo, typeInfo).constData(); +} + +static inline int typeFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + if (!(typeInfo & IsUnresolvedType)) + return typeInfo; + return QMetaType::type(toByteArray(stringData(mo, typeInfo & TypeNameIndexMask))); +} + +class QMetaMethodPrivate : public QMetaMethod +{ +public: + static const QMetaMethodPrivate *get(const QMetaMethod *q) + { return static_cast(q); } + + inline QByteArray signature() const; + inline QByteArray name() const; + inline int typesDataIndex() const; + inline const char *rawReturnTypeName() const; + inline int returnType() const; + inline int parameterCount() const; + inline int parametersDataIndex() const; + inline uint parameterTypeInfo(int index) const; + inline int parameterType(int index) const; + inline void getParameterTypes(int *types) const; + inline QList parameterTypes() const; + inline QList parameterNames() const; + inline QByteArray tag() const; + +private: + QMetaMethodPrivate(); +}; + /*! \since 4.5 @@ -539,7 +586,37 @@ int QMetaObject::classInfoCount() const return n; } +// Returns true if the method defined by the given meta-object&handle +// matches the given name, argument count and argument types, otherwise +// returns false. +static bool methodMatch(const QMetaObject *m, int handle, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + Q_ASSERT(priv(m->d.data)->revision >= 7); + if (int(m->d.data[handle + 1]) != argc) + return false; + + if (toByteArray(stringData(m, m->d.data[handle])) != name) + return false; + + int paramsIndex = m->d.data[handle + 2] + 1; + for (int i = 0; i < argc; ++i) { + uint typeInfo = m->d.data[paramsIndex + i]; + if (types[i].type()) { + if (types[i].type() != typeFromTypeInfo(m, typeInfo)) + return false; + } else { + if (types[i].name() != typeNameFromTypeInfo(m, typeInfo)) + return false; + } + } + + return true; +} + /** \internal +* \obsolete * helper function for indexOf{Method,Slot,Signal}, returns the relative index of the method within * the baseObject * \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. @@ -550,6 +627,8 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, const char *method, bool normalizeStringData) { + QByteArray methodName; + QArgumentTypeArray methodArgumentTypes; for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); @@ -557,10 +636,21 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, ? (priv(m->d.data)->signalCount) : 0; if (!normalizeStringData) { for (; i >= end; --i) { - const char *stringdata = rawStringData(m, m->d.data[priv(m->d.data)->methodData + 5*i]); - if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { - *baseObject = m; - return i; + if (priv(m->d.data)->revision >= 7) { + if (methodName.isEmpty()) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodArgumentTypes); + int handle = priv(m->d.data)->methodData + 5*i; + if (methodMatch(m, handle, methodName, methodArgumentTypes.size(), + methodArgumentTypes.constData())) { + *baseObject = m; + return i; + } + } else { + const char *stringdata = legacyString(m, m->d.data[priv(m->d.data)->methodData + 5*i]); + if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { + *baseObject = m; + return i; + } } } } else if (priv(m->d.data)->revision < 5) { @@ -577,6 +667,34 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, return -1; } +/** \internal +* helper function for indexOf{Method,Slot,Signal}, returns the relative index of the method within +* the baseObject +* \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. +*/ +template +static inline int indexOfMethodRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { + Q_ASSERT(priv(m->d.data)->revision >= 7); + int i = (MethodType == MethodSignal) + ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); + const int end = (MethodType == MethodSlot) + ? (priv(m->d.data)->signalCount) : 0; + + for (; i >= end; --i) { + int handle = priv(m->d.data)->methodData + 5*i; + if (methodMatch(m, handle, name, argc, types)) { + *baseObject = m; + return i; + } + } + } + return -1; +} + /*! \since 4.5 @@ -592,10 +710,16 @@ int QMetaObject::indexOfConstructor(const char *constructor) const { if (priv(d.data)->revision < 2) return -1; - for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { - const char *data = rawStringData(this, d.data[priv(d.data)->constructorData + 5*i]); - if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { - return i; + else if (priv(d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(constructor, types); + return QMetaObjectPrivate::indexOfConstructor(this, name, types.size(), types.constData()); + } else { + for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { + const char *data = legacyString(this, d.data[priv(d.data)->constructorData + 5*i]); + if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { + return i; + } } } return -1; @@ -612,16 +736,60 @@ int QMetaObject::indexOfConstructor(const char *constructor) const int QMetaObject::indexOfMethod(const char *method) const { const QMetaObject *m = this; - int i = indexOfMethodRelative<0>(&m, method, false); - if (i < 0) { - m = this; - i = indexOfMethodRelative<0>(&m, method, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(method, types); + i = indexOfMethodRelative<0>(&m, name, types.size(), types.constData()); + } else { + i = indexOfMethodRelative<0>(&m, method, false); + if (i < 0) { + m = this; + i = indexOfMethodRelative<0>(&m, method, true); + } } if (i >= 0) i += m->methodOffset(); return i; } +// Parses a string of comma-separated types into QArgumentTypes. +static void argumentTypesFromString(const char *str, const char *end, + QArgumentTypeArray &types) +{ + Q_ASSERT(str <= end); + while (str != end) { + if (!types.isEmpty()) + ++str; // Skip comma + const char *begin = str; + int level = 0; + while (str != end && (level > 0 || *str != ',')) { + if (*str == '<') + ++level; + else if (*str == '>') + --level; + ++str; + } + types += QArgumentType(QByteArray(begin, str - begin)); + } +} + +// Given a method \a signature (e.g. "foo(int,double)"), this function +// populates the argument \a types array and returns the method name. +QByteArray QMetaObjectPrivate::decodeMethodSignature( + const char *signature, QArgumentTypeArray &types) +{ + const char *lparens = strchr(signature, '('); + if (!lparens) + return QByteArray(); + const char *rparens = strchr(lparens + 1, ')'); + if (!rparens || *(rparens+1)) + return QByteArray(); + int nameLength = lparens - signature; + argumentTypesFromString(lparens + 1, rparens, types); + return QByteArray::fromRawData(signature, nameLength); +} + /*! Finds \a signal and returns its index; otherwise returns -1. @@ -636,10 +804,17 @@ int QMetaObject::indexOfMethod(const char *method) const int QMetaObject::indexOfSignal(const char *signal) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); - if (i < 0) { - m = this; - i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signal, types); + i = QMetaObjectPrivate::indexOfSignalRelative(&m, name, types.size(), types.constData()); + } else { + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); + if (i < 0) { + m = this; + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + } } if (i >= 0) i += m->methodOffset(); @@ -647,6 +822,7 @@ int QMetaObject::indexOfSignal(const char *signal) const } /*! \internal + \obsolete Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found @@ -668,6 +844,31 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, return i; } +/*! \internal + Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. + + \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found +*/ +int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + int i = indexOfMethodRelative(baseObject, name, argc, types); +#ifndef QT_NO_DEBUG + const QMetaObject *m = *baseObject; + if (i >= 0 && m && m->d.superdata) { + int conflict = indexOfMethod(m->d.superdata, name, argc, types); + if (conflict >= 0) { + QMetaMethod conflictMethod = m->d.superdata->method(conflict); + qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", + conflictMethod.methodSignature().constData(), + rawStringData(m->d.superdata, 0), rawStringData(m, 0)); + } + } + #endif + return i; +} + /*! Finds \a slot and returns its index; otherwise returns -1. @@ -679,9 +880,16 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, int QMetaObject::indexOfSlot(const char *slot) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); - if (i < 0) - i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(slot, types); + i = QMetaObjectPrivate::indexOfSlotRelative(&m, name, types.size(), types.constData()); + } else { + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); + if (i < 0) + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + } if (i >= 0) i += m->methodOffset(); return i; @@ -695,6 +903,104 @@ int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, return indexOfMethodRelative(m, slot, normalizeStringData); } +// same as indexOfSignalRelative but for slots. +int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + return indexOfMethodRelative(m, name, argc, types); +} + +int QMetaObjectPrivate::indexOfSignal(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfSignalRelative(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfSlot(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfSlotRelative(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfMethod(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfMethodRelative<0>(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfConstructor(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + for (int i = priv(m->d.data)->constructorCount-1; i >= 0; --i) { + int handle = priv(m->d.data)->constructorData + 5*i; + if (methodMatch(m, handle, name, argc, types)) + return i; + } + return -1; +} + +/*! + \internal + + Returns true if the \a signalTypes and \a methodTypes are + compatible; otherwise returns false. +*/ +bool QMetaObjectPrivate::checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, + int methodArgc, const QArgumentType *methodTypes) +{ + if (signalArgc < methodArgc) + return false; + for (int i = 0; i < methodArgc; ++i) { + if (signalTypes[i] != methodTypes[i]) + return false; + } + return true; +} + +/*! + \internal + + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. +*/ +bool QMetaObjectPrivate::checkConnectArgs(const QMetaMethodPrivate *signal, + const QMetaMethodPrivate *method) +{ + if (signal->methodType() != QMetaMethod::Signal) + return false; + if (signal->parameterCount() < method->parameterCount()) + return false; + const QMetaObject *smeta = signal->enclosingMetaObject(); + const QMetaObject *rmeta = method->enclosingMetaObject(); + for (int i = 0; i < method->parameterCount(); ++i) { + uint sourceTypeInfo = signal->parameterTypeInfo(i); + uint targetTypeInfo = method->parameterTypeInfo(i); + if ((sourceTypeInfo & IsUnresolvedType) + || (targetTypeInfo & IsUnresolvedType)) { + QByteArray sourceName = typeNameFromTypeInfo(smeta, sourceTypeInfo); + QByteArray targetName = typeNameFromTypeInfo(rmeta, targetTypeInfo); + if (sourceName != targetName) + return false; + } else { + int sourceType = typeFromTypeInfo(smeta, sourceTypeInfo); + int targetType = typeFromTypeInfo(rmeta, targetTypeInfo); + if (sourceType != targetType) + return false; + } + } + return true; +} + static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, const char *name) { while (self) { @@ -872,12 +1178,16 @@ QMetaProperty QMetaObject::property(int index) const if (i >= 0 && i < priv(d.data)->propertyCount) { int handle = priv(d.data)->propertyData + 3*i; int flags = d.data[handle + 2]; - const char *type = rawStringData(this, d.data[handle + 1]); result.mobj = this; result.handle = handle; result.idx = i; if (flags & EnumOrFlag) { + const char *type; + if (priv(d.data)->revision >= 7) + type = rawTypeNameFromTypeInfo(this, d.data[handle + 1]); + else + type = legacyString(this, d.data[handle + 1]); result.menum = enumerator(indexOfEnumerator(type)); if (!result.menum.isValid()) { const char *enum_name = type; @@ -977,6 +1287,21 @@ bool QMetaObject::checkConnectArgs(const char *signal, const char *method) return false; } +/*! + \since 5.0 + \overload + + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. +*/ +bool QMetaObject::checkConnectArgs(const QMetaMethod &signal, + const QMetaMethod &method) +{ + return QMetaObjectPrivate::checkConnectArgs( + QMetaMethodPrivate::get(&signal), + QMetaMethodPrivate::get(&method)); +} + static void qRemoveWhitespace(const char *s, char *d) { char last = 0; @@ -1318,6 +1643,120 @@ bool QMetaObject::invokeMethod(QObject *obj, \internal */ +QByteArray QMetaMethodPrivate::signature() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QByteArray result; + result.reserve(256); + result += name(); + result += '('; + QList argTypes = parameterTypes(); + for (int i = 0; i < argTypes.size(); ++i) { + if (i) + result += ','; + result += argTypes.at(i); + } + result += ')'; + return result; +} + +QByteArray QMetaMethodPrivate::name() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return toByteArray(stringData(mobj, mobj->d.data[handle])); +} + +int QMetaMethodPrivate::typesDataIndex() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[handle + 2]; +} + +const char *QMetaMethodPrivate::rawReturnTypeName() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + uint typeInfo = mobj->d.data[typesDataIndex()]; + if (typeInfo & IsUnresolvedType) + return rawStringData(mobj, typeInfo & TypeNameIndexMask); + else { + if (typeInfo == QMetaType::Void) { + // QMetaMethod::typeName() is documented to return an empty string + // if the return type is void, but QMetaType::typeName() returns + // "void". + return ""; + } + return QMetaType::typeName(typeInfo); + } +} + +int QMetaMethodPrivate::returnType() const +{ + return parameterType(-1); +} + +int QMetaMethodPrivate::parameterCount() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[handle + 1]; +} + +int QMetaMethodPrivate::parametersDataIndex() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return typesDataIndex() + 1; +} + +uint QMetaMethodPrivate::parameterTypeInfo(int index) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[parametersDataIndex() + index]; +} + +int QMetaMethodPrivate::parameterType(int index) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return typeFromTypeInfo(mobj, parameterTypeInfo(index)); +} + +void QMetaMethodPrivate::getParameterTypes(int *types) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + int dataIndex = parametersDataIndex(); + int argc = parameterCount(); + for (int i = 0; i < argc; ++i) { + int id = typeFromTypeInfo(mobj, mobj->d.data[dataIndex++]); + *(types++) = id; + } +} + +QList QMetaMethodPrivate::parameterTypes() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QList list; + int argc = parameterCount(); + int paramsIndex = parametersDataIndex(); + for (int i = 0; i < argc; ++i) + list += typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + i]); + return list; +} + +QList QMetaMethodPrivate::parameterNames() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QList list; + int argc = parameterCount(); + int namesIndex = parametersDataIndex() + argc; + for (int i = 0; i < argc; ++i) + list += toByteArray(stringData(mobj, mobj->d.data[namesIndex + i])); + return list; +} + +QByteArray QMetaMethodPrivate::tag() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return toByteArray(stringData(mobj, mobj->d.data[handle + 3])); +} + /*! \since 5.0 @@ -1330,7 +1769,92 @@ QByteArray QMetaMethod::methodSignature() const { if (!mobj) return QByteArray(); - return toByteArray(stringData(mobj, mobj->d.data[handle])); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->signature(); + } else { + const char *sig = rawStringData(mobj, mobj->d.data[handle]); + return QByteArray::fromRawData(sig, qstrlen(sig)); + } +} + +/*! + \since 5.0 + + Returns the name of this method. + + \sa methodSignature(), parameterCount() +*/ +QByteArray QMetaMethod::name() const +{ + if (!mobj) + return QByteArray(); + return QMetaMethodPrivate::get(this)->name(); +} + +/*! + \since 5.0 + + Returns the return type of this method. + + The return value is one of the types that are registered + with QMetaType, or 0 if the type is not registered. + + \sa parameterType(), QMetaType, typeName() +*/ +int QMetaMethod::returnType() const + { + if (!mobj) + return 0; + return QMetaMethodPrivate::get(this)->returnType(); +} + +/*! + \since 5.0 + + Returns the number of parameters of this method. + + \sa parameterType(), parameterNames() +*/ +int QMetaMethod::parameterCount() const +{ + if (!mobj) + return 0; + return QMetaMethodPrivate::get(this)->parameterCount(); +} + +/*! + \since 5.0 + + Returns the type of the parameter at the given \a index. + + The return value is one of the types that are registered + with QMetaType, or 0 if the type is not registered. + + \sa parameterCount(), returnType(), QMetaType +*/ +int QMetaMethod::parameterType(int index) const +{ + if (!mobj || index < 0) + return 0; + if (index >= QMetaMethodPrivate::get(this)->parameterCount()) + return 0; + return QMetaMethodPrivate::get(this)->parameterType(index); +} + +/*! + \since 5.0 + \internal + + Gets the parameter \a types of this method. The storage + for \a types must be able to hold parameterCount() items. + + \sa parameterCount(), returnType(), parameterType() +*/ +void QMetaMethod::getParameterTypes(int *types) const +{ + if (!mobj) + return; + QMetaMethodPrivate::get(this)->getParameterTypes(types); } /*! @@ -1342,8 +1866,12 @@ QList QMetaMethod::parameterTypes() const { if (!mobj) return QList(); - return QMetaObjectPrivate::parameterTypeNamesFromSignature( - rawStringData(mobj, mobj->d.data[handle])); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->parameterTypes(); + } else { + return QMetaObjectPrivate::parameterTypeNamesFromSignature( + legacyString(mobj, mobj->d.data[handle])); + } } /*! @@ -1356,36 +1884,43 @@ QList QMetaMethod::parameterNames() const QList list; if (!mobj) return list; - const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); - if (*names == 0) { - // do we have one or zero arguments? - const char *signature = rawStringData(mobj, mobj->d.data[handle]); - while (*signature && *signature != '(') - ++signature; - if (*++signature != ')') - list += QByteArray(); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->parameterNames(); } else { - --names; - do { - const char *begin = ++names; - while (*names && *names != ',') - ++names; - list += QByteArray(begin, names - begin); - } while (*names); + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); + if (*names == 0) { + // do we have one or zero arguments? + const char *signature = rawStringData(mobj, mobj->d.data[handle]); + while (*signature && *signature != '(') + ++signature; + if (*++signature != ')') + list += QByteArray(); + } else { + --names; + do { + const char *begin = ++names; + while (*names && *names != ',') + ++names; + list += QByteArray(begin, names - begin); + } while (*names); + } + return list; } - return list; } /*! - Returns the return type of this method, or an empty string if the + Returns the return type name of this method, or an empty string if the return type is \e void. */ const char *QMetaMethod::typeName() const { if (!mobj) return 0; - return rawStringData(mobj, mobj->d.data[handle + 2]); + if (priv(mobj->d.data)->revision >= 7) + return QMetaMethodPrivate::get(this)->rawReturnTypeName(); + else + return legacyString(mobj, mobj->d.data[handle + 2]); } /*! @@ -1422,7 +1957,10 @@ const char *QMetaMethod::tag() const { if (!mobj) return 0; - return rawStringData(mobj, mobj->d.data[handle + 3]); + if (priv(mobj->d.data)->revision >= 7) + return QMetaMethodPrivate::get(this)->tag().constData(); + else + return legacyString(mobj, mobj->d.data[handle + 3]); } @@ -1623,7 +2161,9 @@ bool QMetaMethod::invoke(QObject *object, break; } int metaMethodArgumentCount = 0; - { + if (priv(mobj->d.data)->revision >= 7) { + metaMethodArgumentCount = QMetaMethodPrivate::get(this)->parameterCount(); + } else { // based on QMetaObject::parameterNames() const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); if (*names == 0) { @@ -2058,7 +2598,10 @@ QByteArray QMetaEnum::valueToKeys(int value) const v = v & ~k; if (!keys.isEmpty()) keys += '|'; - keys += rawStringData(mobj, mobj->d.data[data + 2*i]); + if (priv(mobj->d.data)->revision >= 7) + keys += toByteArray(stringData(mobj, mobj->d.data[data + 2*i])); + else + keys += legacyString(mobj, mobj->d.data[data + 2*i]); } } return keys; @@ -2150,7 +2693,10 @@ const char *QMetaProperty::typeName() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return rawStringData(mobj, mobj->d.data[handle + 1]); + if (priv(mobj->d.data)->revision >= 7) + return rawTypeNameFromTypeInfo(mobj, mobj->d.data[handle + 1]); + else + return legacyString(mobj, mobj->d.data[handle + 1]); } /*! @@ -2164,9 +2710,16 @@ QVariant::Type QMetaProperty::type() const if (!mobj) return QVariant::Invalid; int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - uint type = flags >> 24; + uint type; + if (priv(mobj->d.data)->revision >= 7) { + type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); + if (type >= QMetaType::User) + return QVariant::UserType; + } else { + uint flags = mobj->d.data[handle + 2]; + type = flags >> 24; + } if (type) return QVariant::Type(type); if (isEnumType()) { @@ -2194,11 +2747,22 @@ QVariant::Type QMetaProperty::type() const */ int QMetaProperty::userType() const { - QVariant::Type tp = type(); - if (tp != QVariant::UserType) - return tp; + if (!mobj) + return 0; + if (priv(mobj->d.data)->revision >= 7) { + int handle = priv(mobj->d.data)->propertyData + 3*idx; + int type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); + if (type) + return type; + } else { + QVariant::Type tp = type(); + if (tp != QVariant::UserType) + return tp; + } if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + if (enumMetaTypeId == 0) + return QVariant::Int; // Match behavior of QMetaType::type() return enumMetaTypeId; } return QMetaType::type(typeName()); @@ -2298,13 +2862,25 @@ QVariant QMetaProperty::read(const QObject *object) const t = enumMetaTypeId; } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); - t = (flags >> 24); - if (t == QVariant::Invalid) - t = QMetaType::type(typeName); - if (t == QVariant::Invalid) - t = QVariant::nameToType(typeName); + const char *typeName = 0; + if (priv(mobj->d.data)->revision >= 7) { + uint typeInfo = mobj->d.data[handle + 1]; + if (!(typeInfo & IsUnresolvedType)) + t = typeInfo; + else { + typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask); + t = QMetaType::type(typeName); + } + } else { + uint flags = mobj->d.data[handle + 2]; + t = (flags >> 24); + if (t == QVariant::Invalid) { + typeName = legacyString(mobj, mobj->d.data[handle + 1]); + t = QMetaType::type(typeName); + if (t == QVariant::Invalid) + t = QVariant::nameToType(typeName); + } + } if (t == QVariant::Invalid) { qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property '%s::%s'", typeName, mobj->className(), name()); return QVariant(); @@ -2367,8 +2943,20 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const v.convert(QVariant::Int); } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - t = flags >> 24; + const char *typeName = 0; + if (priv(mobj->d.data)->revision >= 7) { + uint typeInfo = mobj->d.data[handle + 1]; + if (!(typeInfo & IsUnresolvedType)) + t = typeInfo; + else { + typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask); + t = QMetaType::type(typeName); + } + } else { + uint flags = mobj->d.data[handle + 2]; + t = flags >> 24; + typeName = legacyString(mobj, mobj->d.data[handle + 1]); + } if (t == QVariant::Invalid) { const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); const char *vtypeName = value.typeName(); diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 573e69fdb7..095b196dca 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -58,7 +58,12 @@ public: inline QMetaMethod() : mobj(0),handle(0) {} QByteArray methodSignature() const; + QByteArray name() const; const char *typeName() const; + int returnType() const; + int parameterCount() const; + int parameterType(int index) const; + void getParameterTypes(int *types) const; QList parameterTypes() const; QList parameterNames() const; const char *tag() const; @@ -146,6 +151,7 @@ private: const QMetaObject *mobj; uint handle; + friend class QMetaMethodPrivate; friend struct QMetaObject; friend struct QMetaObjectPrivate; friend class QObject; diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index d789711bb4..509dede4cb 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -105,6 +105,59 @@ enum MetaObjectFlags { RequiresVariantMetaObject = 0x02 }; +enum MetaDataFlags { + IsUnresolvedType = 0x80000000, + TypeNameIndexMask = 0x7FFFFFFF +}; + +class QArgumentType +{ +public: + QArgumentType(int type) + : _type(type) + {} + QArgumentType(const QByteArray &name) + : _type(QMetaType::type(name.constData())), _name(name) + {} + QArgumentType() + : _type(0) + {} + int type() const + { return _type; } + QByteArray name() const + { + if (_type && _name.isEmpty()) + const_cast(this)->_name = QMetaType::typeName(_type); + return _name; + } + bool operator==(const QArgumentType &other) const + { + if (_type) + return _type == other._type; + else if (other._type) + return false; + else + return _name == other._name; + } + bool operator!=(const QArgumentType &other) const + { + if (_type) + return _type != other._type; + else if (other._type) + return true; + else + return _name != other._name; + } + +private: + int _type; + QByteArray _name; +}; + +template class QVarLengthArray; +typedef QVarLengthArray QArgumentTypeArray; + +class QMetaMethodPrivate; class QMutex; struct QMetaObjectPrivate @@ -137,6 +190,27 @@ struct QMetaObjectPrivate bool normalizeStringData); static int originalClone(const QMetaObject *obj, int local_method_index); + static QByteArray decodeMethodSignature(const char *signature, + QArgumentTypeArray &types); + static int indexOfSignalRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types); + static int indexOfSlotRelative(const QMetaObject **m, + const QByteArray &name, int argc, + const QArgumentType *types); + static int indexOfSignal(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfSlot(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfMethod(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfConstructor(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, + int methodArgc, const QArgumentType *methodTypes); + static bool checkConnectArgs(const QMetaMethodPrivate *signal, + const QMetaMethodPrivate *method); + static QList parameterTypeNamesFromSignature(const char *signature); #ifndef QT_NO_QOBJECT diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index f44e4c4761..9977f96f99 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -95,6 +95,30 @@ static int *queuedConnectionTypes(const QList &typeNames) return types; } +static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc) +{ + QScopedArrayPointer types(new int [argc + 1]); + for (int i = 0; i < argc; ++i) { + const QArgumentType &type = argumentTypes[i]; + if (type.type()) + types[i] = type.type(); + else if (type.name().endsWith('*')) + types[i] = QMetaType::VoidStar; + else + types[i] = QMetaType::type(type.name()); + + if (!types[i]) { + qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" + "(Make sure '%s' is registered using qRegisterMetaType().)", + type.name().constData(), type.name().constData()); + return 0; + } + } + types[argc] = 0; + + return types.take(); +} + static QBasicMutex _q_ObjectMutexPool[131]; /** \internal @@ -2278,20 +2302,40 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code - int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - if (signal_index < 0) { - // check for normalized signatures - tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); - signal = tmp_signal_name.constData() + 1; + QByteArray signalName; + QArgumentTypeArray signalTypes; + int signal_index; + if (QMetaObjectPrivate::get(smeta)->revision >= 7) { + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + if (signal_index < 0) { + // check for normalized signatures + tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); + signal = tmp_signal_name.constData() + 1; - smeta = sender->metaObject(); + signalTypes.clear(); + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + } + } else { signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - } - if (signal_index < 0) { - // re-use tmp_signal_name and signal from above + if (signal_index < 0) { + // check for normalized signatures + tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); + signal = tmp_signal_name.constData() + 1; - smeta = sender->metaObject(); - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) { + // re-use tmp_signal_name and signal from above + + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } + } } if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); @@ -2312,36 +2356,71 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign const char *method_arg = method; ++method; // skip code + QByteArray methodName; + QArgumentTypeArray methodTypes; const QMetaObject *rmeta = receiver->metaObject(); int method_index_relative = -1; - switch (membcode) { - case QSLOT_CODE: - method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); - break; - case QSIGNAL_CODE: - method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); - break; - } + if (QMetaObjectPrivate::get(rmeta)->revision >= 7) { + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + } + if (method_index_relative < 0) { + // check for normalized methods + tmp_method_name = QMetaObject::normalizedSignature(method); + method = tmp_method_name.constData(); - if (method_index_relative < 0) { - // check for normalized methods - tmp_method_name = QMetaObject::normalizedSignature(method); - method = tmp_method_name.constData(); - - // rmeta may have been modified above - rmeta = receiver->metaObject(); + methodTypes.clear(); + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); + // rmeta may have been modified above + rmeta = receiver->metaObject(); + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + } + } + } else { switch (membcode) { case QSLOT_CODE: method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); - if (method_index_relative < 0) - method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); break; case QSIGNAL_CODE: method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); - if (method_index_relative < 0) - method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); break; } + + if (method_index_relative < 0) { + // check for normalized methods + tmp_method_name = QMetaObject::normalizedSignature(method); + method = tmp_method_name.constData(); + + // rmeta may have been modified above + rmeta = receiver->metaObject(); + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); + break; + } + } } if (method_index_relative < 0) { @@ -2350,7 +2429,18 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign return QMetaObject::Connection(0); } - if (!QMetaObject::checkConnectArgs(signal, method)) { + bool compatibleArgs = true; + if ((QMetaObjectPrivate::get(smeta)->revision < 7) && (QMetaObjectPrivate::get(rmeta)->revision < 7)) { + compatibleArgs = QMetaObject::checkConnectArgs(signal, method); + } else { + if (signalName.isEmpty()) + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + if (methodName.isEmpty()) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); + compatibleArgs = QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(), + methodTypes.size(), methodTypes.constData()); + } + if (!compatibleArgs) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, @@ -2360,8 +2450,11 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign int *types = 0; if ((type == Qt::QueuedConnection) - && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) + && ((QMetaObjectPrivate::get(smeta)->revision >= 7) + ? !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size())) + : !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))) { return QMetaObject::Connection(0); + } #ifndef QT_NO_DEBUG if (warnCompat) { @@ -2604,12 +2697,25 @@ bool QObject::disconnect(const QObject *sender, const char *signal, */ bool res = false; const QMetaObject *smeta = sender->metaObject(); + QByteArray signalName; + QArgumentTypeArray signalTypes; + if (signal && (QMetaObjectPrivate::get(smeta)->revision >= 7)) + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + QByteArray methodName; + QArgumentTypeArray methodTypes; + if (method && (QMetaObjectPrivate::get(receiver->metaObject())->revision >= 7)) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); do { int signal_index = -1; if (signal) { - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - if (signal_index < 0) - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + if (QMetaObjectPrivate::get(smeta)->revision >= 7) { + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + } else { + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } if (signal_index < 0) break; signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); @@ -2624,7 +2730,13 @@ bool QObject::disconnect(const QObject *sender, const char *signal, } else { const QMetaObject *rmeta = receiver->metaObject(); do { - int method_index = rmeta->indexOfMethod(method); + int method_index; + if (QMetaObjectPrivate::get(rmeta)->revision >= 7) { + method_index = QMetaObjectPrivate::indexOfMethod( + rmeta, methodName, methodTypes.size(), methodTypes.constData()); + } else { + method_index = rmeta->indexOfMethod(method); + } if (method_index >= 0) while (method_index < rmeta->methodOffset()) rmeta = rmeta->superClass(); @@ -3307,9 +3419,17 @@ int QObjectPrivate::signalIndex(const char *signalName) const { Q_Q(const QObject); const QMetaObject *base = q->metaObject(); - int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); - if (relative_index < 0) - relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); + int relative_index; + if (QMetaObjectPrivate::get(base)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signalName, types); + relative_index = QMetaObjectPrivate::indexOfSignalRelative( + &base, name, types.size(), types.constData()); + } else { + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); + if (relative_index < 0) + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); + } if (relative_index < 0) return relative_index; relative_index = QMetaObjectPrivate::originalClone(base, relative_index); @@ -4037,9 +4157,16 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa locker.unlock(); // reconstruct the signature to call connectNotify - const char *sig = QMetaObjectPrivate::rawStringData(senderMetaObject, senderMetaObject->d.data[ - reinterpret_cast(senderMetaObject->d.data)->methodData - + 5 * (signal_index - signalOffset)]); + QByteArray tmp_sig; + const char *sig; + if (QMetaObjectPrivate::get(senderMetaObject)->revision >= 7) { + tmp_sig = senderMetaObject->method(signal_index - signalOffset + methodOffset).methodSignature(); + sig = tmp_sig.constData(); + } else { + sig = reinterpret_cast(senderMetaObject->d.stringdata) + + senderMetaObject->d.data[QMetaObjectPrivate::get(senderMetaObject)->methodData + + 5 * (signal_index - signalOffset)]; + } QVarLengthArray signalSignature(qstrlen(sig) + 2); signalSignature.data()[0] = char(QSIGNAL_CODE + '0'); strcpy(signalSignature.data() + 1 , sig); diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 6c40733877..8751d0f5a1 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -325,6 +325,8 @@ struct Q_CORE_EXPORT QMetaObject QMetaProperty userProperty() const; static bool checkConnectArgs(const char *signal, const char *method); + static bool checkConnectArgs(const QMetaMethod &signal, + const QMetaMethod &method); static QByteArray normalizedSignature(const char *method); static QByteArray normalizedType(const char *type); diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 9571af4bd5..73a0605028 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE -uint qvariant_nameToType(const QByteArray &name) +uint nameToBuiltinType(const QByteArray &name) { if (name.isEmpty()) return 0; @@ -64,20 +64,27 @@ uint qvariant_nameToType(const QByteArray &name) } /* - Returns true if the type is a QVariant types. + Returns true if the type is a built-in type. */ -bool isVariantType(const QByteArray &type) -{ - return qvariant_nameToType(type) != 0; +bool isBuiltinType(const QByteArray &type) + { + int id = QMetaType::type(type.constData()); + if (!id && !type.isEmpty() && type != "void") + return false; + return (id < QMetaType::User); } -/*! - Returns true if the type is qreal. -*/ -static bool isQRealType(const QByteArray &type) -{ - return (type == "qreal"); -} +static const char *metaTypeEnumValueString(int type) + { +#define RETURN_METATYPENAME_STRING(MetaTypeName, MetaTypeId, RealType) \ + case QMetaType::MetaTypeName: return #MetaTypeName; + + switch (type) { +QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) + } +#undef RETURN_METATYPENAME_STRING + return 0; + } Generator::Generator(ClassDef *classDef, const QList &metaTypes, FILE *outfile) : out(outfile), cdef(classDef), metaTypes(metaTypes) @@ -122,6 +129,17 @@ int Generator::stridx(const QByteArray &s) return i; } +// Returns the sum of all parameters (including return type) for the given +// \a list of methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +static int aggregateParameterCount(const QList &list) +{ + int sum = 0; + for (int i = 0; i < list.count(); ++i) + sum += list.at(i).arguments.count() + 1; // +1 for return type + return sum; +} + void Generator::generateCode() { bool isQt = (cdef->classname == "Qt"); @@ -266,6 +284,15 @@ void Generator::generateCode() index += methodCount * 5; if (cdef->revisionedMethods) index += methodCount; + int paramsIndex = index; + int totalParameterCount = aggregateParameterCount(cdef->signalList) + + aggregateParameterCount(cdef->slotList) + + aggregateParameterCount(cdef->methodList) + + aggregateParameterCount(cdef->constructorList); + index += totalParameterCount * 2 // types and parameter names + - methodCount // return "parameters" don't have names + - cdef->constructorList.count(); // "this" parameters don't have names + fprintf(out, " %4d, %4d, // properties\n", cdef->propertyList.count(), cdef->propertyList.count() ? index : 0); index += cdef->propertyList.count() * 3; if(cdef->notifyableProperties) @@ -292,17 +319,17 @@ void Generator::generateCode() // // Build signals array first, otherwise the signal indices would be wrong // - generateFunctions(cdef->signalList, "signal", MethodSignal); + generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex); // // Build slots array // - generateFunctions(cdef->slotList, "slot", MethodSlot); + generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex); // // Build method array // - generateFunctions(cdef->methodList, "method", MethodMethod); + generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex); // // Build method version arrays @@ -313,6 +340,15 @@ void Generator::generateCode() generateFunctionRevisions(cdef->methodList, "method"); } +// +// Build method parameters array +// + generateFunctionParameters(cdef->signalList, "signal"); + generateFunctionParameters(cdef->slotList, "slot"); + generateFunctionParameters(cdef->methodList, "method"); + if (isConstructible) + generateFunctionParameters(cdef->constructorList, "constructor"); + // // Build property array // @@ -327,7 +363,7 @@ void Generator::generateCode() // Build constructors array // if (isConstructible) - generateFunctions(cdef->constructorList, "constructor", MethodConstructor); + generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex); // // Terminate data array @@ -346,7 +382,7 @@ void Generator::generateCode() QList extraList; for (int i = 0; i < cdef->propertyList.count(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); - if (!isVariantType(p.type) && !metaTypes.contains(p.type) && !p.type.contains('*') && + if (!isBuiltinType(p.type) && !metaTypes.contains(p.type) && !p.type.contains('*') && !p.type.contains('<') && !p.type.contains('>')) { int s = p.type.lastIndexOf("::"); if (s > 0) { @@ -511,50 +547,29 @@ void Generator::registerFunctionStrings(const QList& list) for (int i = 0; i < list.count(); ++i) { const FunctionDef &f = list.at(i); - QByteArray sig = f.name + '('; - QByteArray arguments; + strreg(f.name); + if (!isBuiltinType(f.normalizedType)) + strreg(f.normalizedType); + strreg(f.tag); for (int j = 0; j < f.arguments.count(); ++j) { const ArgumentDef &a = f.arguments.at(j); - if (j) { - sig += ","; - arguments += ","; - } - sig += a.normalizedType; - arguments += a.name; + if (!isBuiltinType(a.normalizedType)) + strreg(a.normalizedType); + strreg(a.name); } - sig += ')'; - - strreg(sig); - strreg(arguments); - strreg(f.normalizedType); - strreg(f.tag); } } -void Generator::generateFunctions(const QList& list, const char *functype, int type) +void Generator::generateFunctions(const QList& list, const char *functype, int type, int ¶msIndex) { if (list.isEmpty()) return; - fprintf(out, "\n // %ss: signature, parameters, type, tag, flags\n", functype); + fprintf(out, "\n // %ss: name, argc, parameters, tag, flags\n", functype); for (int i = 0; i < list.count(); ++i) { const FunctionDef &f = list.at(i); - QByteArray sig = f.name + '('; - QByteArray arguments; - - for (int j = 0; j < f.arguments.count(); ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) { - sig += ","; - arguments += ","; - } - sig += a.normalizedType; - arguments += a.name; - } - sig += ')'; - unsigned char flags = type; if (f.access == FunctionDef::Private) flags |= AccessPrivate; @@ -576,8 +591,12 @@ void Generator::generateFunctions(const QList& list, const char *fu flags |= MethodScriptable; if (f.revision > 0) flags |= MethodRevisioned; - fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", stridx(sig), - stridx(arguments), stridx(f.normalizedType), stridx(f.tag), flags); + + int argc = f.arguments.count(); + fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", + stridx(f.name), argc, paramsIndex, stridx(f.tag), flags); + + paramsIndex += 1 + argc * 2; } } @@ -591,12 +610,51 @@ void Generator::generateFunctionRevisions(const QList& list, const } } +void Generator::generateFunctionParameters(const QList& list, const char *functype) +{ + if (list.isEmpty()) + return; + fprintf(out, "\n // %ss: parameters\n", functype); + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + fprintf(out, " "); + + // Types + for (int j = -1; j < f.arguments.count(); ++j) { + if (j > -1) + fputc(' ', out); + const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType; + if (isBuiltinType(typeName)) { + int type = nameToBuiltinType(typeName); + const char *valueString = metaTypeEnumValueString(type); + if (valueString) + fprintf(out, "QMetaType::%s", valueString); + else + fprintf(out, "%4d", type); + } else { + Q_ASSERT(!typeName.isEmpty()); + fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(typeName)); + } + fputc(',', out); + } + + // Parameter names + for (int j = 0; j < f.arguments.count(); ++j) { + const ArgumentDef &arg = f.arguments.at(j); + fprintf(out, " %4d,", stridx(arg.name)); + } + + fprintf(out, "\n"); + } +} + void Generator::registerPropertyStrings() { for (int i = 0; i < cdef->propertyList.count(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); strreg(p.name); - strreg(p.type); + if (!isBuiltinType(p.type)) + strreg(p.type); } } @@ -611,11 +669,8 @@ void Generator::generateProperties() for (int i = 0; i < cdef->propertyList.count(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); uint flags = Invalid; - if (!isVariantType(p.type)) { + if (!isBuiltinType(p.type)) flags |= EnumOrFlag; - } else if (!isQRealType(p.type)) { - flags |= qvariant_nameToType(p.type) << 24; - } if (!p.read.isEmpty()) flags |= Readable; if (!p.write.isEmpty()) { @@ -665,12 +720,20 @@ void Generator::generateProperties() if (p.final) flags |= Final; - fprintf(out, " %4d, %4d, ", - stridx(p.name), - stridx(p.type)); - if (!(flags >> 24) && isQRealType(p.type)) - fprintf(out, "(QMetaType::QReal << 24) | "); - fprintf(out, "0x%.8x,\n", flags); + fprintf(out, " %4d, ", stridx(p.name)); + + if (isBuiltinType(p.type)) { + int type = nameToBuiltinType(p.type); + const char *valueString = metaTypeEnumValueString(type); + if (valueString) + fprintf(out, "QMetaType::%s", valueString); + else + fprintf(out, "%4d", type); + } else { + fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(p.type)); + } + + fprintf(out, ", 0x%.8x,\n", flags); } if(cdef->notifyableProperties) { diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index c5692f25ae..c85d24fd15 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -58,8 +58,9 @@ private: void registerClassInfoStrings(); void generateClassInfos(); void registerFunctionStrings(const QList &list); - void generateFunctions(const QList &list, const char *functype, int type); + void generateFunctions(const QList &list, const char *functype, int type, int ¶msIndex); void generateFunctionRevisions(const QList& list, const char *functype); + void generateFunctionParameters(const QList &list, const char *functype); void registerEnumStrings(); void generateEnums(int index); void registerPropertyStrings(); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 385390d954..49fc29592d 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -819,8 +819,7 @@ void Moc::generate(FILE *out) fprintf(out, "#include \n"); fprintf(out, "#include \n"); // For QByteArrayData - if (mustIncludeQMetaTypeH) - fprintf(out, "#include \n"); + fprintf(out, "#include \n"); // For QMetaType::Type if (mustIncludeQPluginH) fprintf(out, "#include \n"); @@ -977,8 +976,6 @@ void Moc::createPropertyDef(PropertyDef &propDef) type = "qlonglong"; else if (type == "ULongLong") type = "qulonglong"; - else if (type == "qreal") - mustIncludeQMetaTypeH = true; propDef.type = type; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index aedb97b234..e20e29acb8 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -198,14 +198,13 @@ class Moc : public Parser { public: Moc() - : noInclude(false), generatedCode(false), mustIncludeQMetaTypeH(false), mustIncludeQPluginH(false) + : noInclude(false), generatedCode(false), mustIncludeQPluginH(false) {} QByteArray filename; bool noInclude; bool generatedCode; - bool mustIncludeQMetaTypeH; bool mustIncludeQPluginH; QByteArray includePath; QList includeFiles; diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index f653e66bfd..60c8fdb2f2 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -603,15 +603,51 @@ void tst_QMetaMethod::method() QCOMPARE(method.methodType(), methodType); QCOMPARE(method.access(), access); - QCOMPARE(method.methodSignature(), signature); + QVERIFY(!method.methodSignature().isEmpty()); + if (method.methodSignature() != signature) { + // QMetaMethod should always produce a semantically equivalent signature + int signatureIndex = (methodType == QMetaMethod::Constructor) + ? mo->indexOfConstructor(method.methodSignature()) + : mo->indexOfMethod(method.methodSignature()); + QCOMPARE(signatureIndex, index); + } + + QByteArray computedName = signature.left(signature.indexOf('(')); + QCOMPARE(method.name(), computedName); QCOMPARE(method.tag(), ""); - QCOMPARE(method.typeName(), returnTypeName.constData()); - QCOMPARE(QMetaType::type(method.typeName()), returnType); + QCOMPARE(method.returnType(), returnType); + if (QByteArray(method.typeName()) != returnTypeName) { + // QMetaMethod should always produce a semantically equivalent typename + QCOMPARE(QMetaType::type(method.typeName()), QMetaType::type(returnTypeName)); + } - QCOMPARE(method.parameterTypes(), parameterTypeNames); + if (method.parameterTypes() != parameterTypeNames) { + // QMetaMethod should always produce semantically equivalent typenames + QList actualTypeNames = method.parameterTypes(); + QCOMPARE(actualTypeNames.size(), parameterTypeNames.size()); + for (int i = 0; i < parameterTypeNames.size(); ++i) { + QCOMPARE(QMetaType::type(actualTypeNames.at(i)), + QMetaType::type(parameterTypeNames.at(i))); + } + } QCOMPARE(method.parameterNames(), parameterNames); + + QCOMPARE(method.parameterCount(), parameterTypes.size()); + for (int i = 0; i < parameterTypes.size(); ++i) + QCOMPARE(method.parameterType(i), parameterTypes.at(i)); + + { + QVector actualParameterTypes(parameterTypes.size()); + method.getParameterTypes(actualParameterTypes.data()); + for (int i = 0; i < parameterTypes.size(); ++i) + QCOMPARE(actualParameterTypes.at(i), parameterTypes.at(i)); + } + + // Bogus indexes + QCOMPARE(method.parameterType(-1), 0); + QCOMPARE(method.parameterType(parameterTypes.size()), 0); } void tst_QMetaMethod::invalidMethod() From 2632f76b421d50cb2135c796a379293007583a1d Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sat, 18 Feb 2012 20:37:50 +0100 Subject: [PATCH 61/74] Port QMetaObjectBuilder to new meta-object string data format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring QMetaObjectBuilder up-to-date with latest moc changes (generating the string table as an array of QByteArrayData). Change-Id: I5b2f63daa687e9bc8eab10a53fab2d72e4529ea2 Reviewed-by: Thiago Macieira Reviewed-by: João Abecasis --- src/corelib/kernel/qmetaobjectbuilder.cpp | 106 +++++++++++++++------- 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 82c74a34f3..3aad49b2f9 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -1073,33 +1073,78 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name) class MetaStringTable { public: - typedef QHash Entries; // string --> offset mapping - typedef Entries::const_iterator const_iterator; - Entries::const_iterator constBegin() const - { return m_entries.constBegin(); } - Entries::const_iterator constEnd() const - { return m_entries.constEnd(); } + MetaStringTable() : m_index(0) {} - MetaStringTable() : m_offset(0) {} + int enter(const QByteArray &value); - int enter(const QByteArray &value) - { - Entries::iterator it = m_entries.find(value); - if (it != m_entries.end()) - return it.value(); - int pos = m_offset; - m_entries.insert(value, pos); - m_offset += value.size() + 1; - return pos; - } - - int arraySize() const { return m_offset; } + static int preferredAlignment(); + int blobSize() const; + void writeBlob(char *out); private: + typedef QHash Entries; // string --> index mapping Entries m_entries; - int m_offset; + int m_index; }; +// Enters the given value into the string table (if it hasn't already been +// entered). Returns the index of the string. +int MetaStringTable::enter(const QByteArray &value) +{ + Entries::iterator it = m_entries.find(value); + if (it != m_entries.end()) + return it.value(); + int pos = m_index; + m_entries.insert(value, pos); + ++m_index; + return pos; +} + +int MetaStringTable::preferredAlignment() +{ +#ifdef Q_ALIGNOF + return Q_ALIGNOF(QByteArrayData); +#else + return sizeof(void *); +#endif +} + +// Returns the size (in bytes) required for serializing this string table. +int MetaStringTable::blobSize() const +{ + int size = m_entries.size() * sizeof(QByteArrayData); + Entries::const_iterator it; + for (it = m_entries.constBegin(); it != m_entries.constEnd(); ++it) + size += it.key().size() + 1; + return size; +} + +// Writes strings to string data struct. +// The struct consists of an array of QByteArrayData, followed by a char array +// containing the actual strings. This format must match the one produced by +// moc (see generator.cpp). +void MetaStringTable::writeBlob(char *out) +{ + Q_ASSERT(!(reinterpret_cast(out) & (preferredAlignment()-1))); + + int offsetOfStringdataMember = m_entries.size() * sizeof(QByteArrayData); + int stringdataOffset = 0; + for (int i = 0; i < m_entries.size(); ++i) { + const QByteArray &str = m_entries.key(i); + int size = str.size(); + qptrdiff offset = offsetOfStringdataMember + stringdataOffset + - i * sizeof(QByteArrayData); + const QByteArrayData data = { Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset }; + + memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData)); + + memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size); + out[offsetOfStringdataMember + stringdataOffset + size] = '\0'; + + stringdataOffset += size + 1; + } +} + // Build the parameter array string for a method. static QByteArray buildParameterNames (const QByteArray& signature, const QList& parameterNames) @@ -1182,7 +1227,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, } } if (buf) { - Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 6, "QMetaObjectBuilder should generate the same version as moc"); + Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; pmeta->flags = d->flags; pmeta->className = 0; // Class name is always the first string. @@ -1240,13 +1285,14 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Find the start of the data and string tables. int *data = reinterpret_cast(pmeta); size += dataIndex * sizeof(int); + ALIGN(size, void *); char *str = reinterpret_cast(buf + size); if (buf) { if (relocatable) { - meta->d.stringdata = reinterpret_cast((quintptr)size); + meta->d.stringdata = reinterpret_cast((quintptr)size); meta->d.data = reinterpret_cast((quintptr)pmetaSize); } else { - meta->d.stringdata = str; + meta->d.stringdata = reinterpret_cast(str); meta->d.data = reinterpret_cast(data); } } @@ -1390,16 +1436,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5; } - size += strings.arraySize(); + size += strings.blobSize(); - if (buf) { - // Write strings to string data array. - MetaStringTable::const_iterator it; - for (it = strings.constBegin(); it != strings.constEnd(); ++it) { - memcpy(str + it.value(), it.key().constData(), it.key().size()); - str[it.value() + it.key().size()] = '\0'; - } - } + if (buf) + strings.writeBlob(str); // Output the zero terminator in the data array. if (buf) @@ -1508,7 +1548,7 @@ void QMetaObjectBuilder::fromRelocatableData(QMetaObject *output, quintptr dataOffset = (quintptr)dataMo->d.data; output->d.superdata = superclass; - output->d.stringdata = buf + stringdataOffset; + output->d.stringdata = reinterpret_cast(buf + stringdataOffset); output->d.data = reinterpret_cast(buf + dataOffset); output->d.extradata = 0; } From 69e3e544864e55ebe42df035daf3bf66e25c820f Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Mon, 6 Feb 2012 20:42:33 +0100 Subject: [PATCH 62/74] Add QMetaMethodBuilder::parameterTypes() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function matches QMetaMethod::parameterTypes(). The implementation of QMetaMethod::parameterTypes() was moved to a helper function in QMetaObjectPrivate, so that it can be shared with QMetaMethodBuilder. Change-Id: I4361713996dc4ea31a79c2fc74c813ee5e9c3069 Reviewed-by: Thiago Macieira Reviewed-by: João Abecasis --- src/corelib/kernel/qmetaobjectbuilder.cpp | 21 ++++++++++++++++- src/corelib/kernel/qmetaobjectbuilder_p.h | 1 + .../tst_qmetaobjectbuilder.cpp | 23 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 3aad49b2f9..ea8fba10d0 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -136,6 +136,11 @@ public: { attributes = ((attributes & ~AccessMask) | (int)value); } + + QList parameterTypes() const + { + return QMetaObjectPrivate::parameterTypeNamesFromSignature(signature); + } }; class QMetaPropertyBuilderPrivate @@ -1936,7 +1941,7 @@ QByteArray QMetaMethodBuilder::returnType() const is empty, then the method's return type is \c{void}. The \a value will be normalized before it is added to the method. - \sa returnType(), signature() + \sa returnType(), parameterTypes(), signature() */ void QMetaMethodBuilder::setReturnType(const QByteArray& value) { @@ -1945,6 +1950,20 @@ void QMetaMethodBuilder::setReturnType(const QByteArray& value) d->returnType = QMetaObject::normalizedType(value); } +/*! + Returns the list of parameter types for this method. + + \sa returnType(), parameterNames() +*/ +QList QMetaMethodBuilder::parameterTypes() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->parameterTypes(); + else + return QList(); +} + /*! Returns the list of parameter names for this method. diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index 86bc354164..ef802ce82b 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -203,6 +203,7 @@ public: QByteArray returnType() const; void setReturnType(const QByteArray& value); + QList parameterTypes() const; QList parameterNames() const; void setParameterNames(const QList& value); diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 28794050c9..f187425c84 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -217,6 +217,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(nullMethod.signature(), QByteArray()); QVERIFY(nullMethod.methodType() == QMetaMethod::Method); QVERIFY(nullMethod.returnType().isEmpty()); + QVERIFY(nullMethod.parameterTypes().isEmpty()); QVERIFY(nullMethod.parameterNames().isEmpty()); QVERIFY(nullMethod.tag().isEmpty()); QVERIFY(nullMethod.access() == QMetaMethod::Public); @@ -229,6 +230,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.parameterTypes(), QList() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Public); @@ -242,6 +244,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("int")); + QCOMPARE(method2.parameterTypes(), QList() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -267,6 +270,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterTypes(), QList() << "QString" << "int"); QCOMPARE(method1.parameterNames(), QList() << "a" << "b"); QCOMPARE(method1.tag(), QByteArray("tag")); QVERIFY(method1.access() == QMetaMethod::Private); @@ -276,6 +280,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("int")); + QCOMPARE(method2.parameterTypes(), QList() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -296,6 +301,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterTypes(), QList() << "QString" << "int"); QCOMPARE(method1.parameterNames(), QList() << "a" << "b"); QCOMPARE(method1.tag(), QByteArray("tag")); QVERIFY(method1.access() == QMetaMethod::Private); @@ -305,6 +311,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterTypes(), QList() << "QString"); QCOMPARE(method2.parameterNames(), QList() << "c"); QCOMPARE(method2.tag(), QByteArray("Q_FOO")); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -320,6 +327,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterTypes(), QList() << "QString"); QCOMPARE(method2.parameterNames(), QList() << "c"); QCOMPARE(method2.tag(), QByteArray("Q_FOO")); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -347,6 +355,7 @@ void tst_QMetaObjectBuilder::slot() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Slot); QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.parameterTypes(), QList() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Public); @@ -359,6 +368,7 @@ void tst_QMetaObjectBuilder::slot() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Slot); QVERIFY(method2.returnType().isEmpty()); + QCOMPARE(method2.parameterTypes(), QList() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -384,6 +394,7 @@ void tst_QMetaObjectBuilder::signal() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Signal); QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.parameterTypes(), QList() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Protected); @@ -396,6 +407,7 @@ void tst_QMetaObjectBuilder::signal() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Signal); QVERIFY(method2.returnType().isEmpty()); + QCOMPARE(method2.parameterTypes(), QList() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -421,6 +433,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QVERIFY(ctor1.returnType().isEmpty()); + QCOMPARE(ctor1.parameterTypes(), QList() << "QString" << "int"); QVERIFY(ctor1.parameterNames().isEmpty()); QVERIFY(ctor1.tag().isEmpty()); QVERIFY(ctor1.access() == QMetaMethod::Public); @@ -433,6 +446,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QVERIFY(ctor2.returnType().isEmpty()); + QCOMPARE(ctor2.parameterTypes(), QList() << "QString"); QVERIFY(ctor2.parameterNames().isEmpty()); QVERIFY(ctor2.tag().isEmpty()); QVERIFY(ctor2.access() == QMetaMethod::Public); @@ -458,6 +472,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterTypes(), QList() << "QString" << "int"); QCOMPARE(ctor1.parameterNames(), QList() << "a" << "b"); QCOMPARE(ctor1.tag(), QByteArray("tag")); QVERIFY(ctor1.access() == QMetaMethod::Private); @@ -466,6 +481,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QVERIFY(ctor2.returnType().isEmpty()); + QCOMPARE(ctor2.parameterTypes(), QList() << "QString"); QVERIFY(ctor2.parameterNames().isEmpty()); QVERIFY(ctor2.tag().isEmpty()); QVERIFY(ctor2.access() == QMetaMethod::Public); @@ -484,6 +500,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterTypes(), QList() << "QString" << "int"); QCOMPARE(ctor1.parameterNames(), QList() << "a" << "b"); QCOMPARE(ctor1.tag(), QByteArray("tag")); QVERIFY(ctor1.access() == QMetaMethod::Private); @@ -492,6 +509,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterTypes(), QList() << "QString"); QCOMPARE(ctor2.parameterNames(), QList() << "c"); QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); QVERIFY(ctor2.access() == QMetaMethod::Protected); @@ -506,6 +524,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterTypes(), QList() << "QString"); QCOMPARE(ctor2.parameterNames(), QList() << "c"); QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); QVERIFY(ctor2.access() == QMetaMethod::Protected); @@ -525,6 +544,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(prototypeConstructor.signature(), QByteArray("SomethingOfEverything()")); QVERIFY(prototypeConstructor.methodType() == QMetaMethod::Constructor); QCOMPARE(prototypeConstructor.returnType(), QByteArray()); + QVERIFY(prototypeConstructor.parameterTypes().isEmpty()); QVERIFY(prototypeConstructor.access() == QMetaMethod::Public); QCOMPARE(prototypeConstructor.index(), 1); @@ -1167,6 +1187,9 @@ static bool sameMethod(const QMetaMethod& method1, const QMetaMethod& method2) if (QByteArray(method1.typeName()) != QByteArray(method2.typeName())) return false; + if (method1.parameterTypes() != method2.parameterTypes()) + return false; + if (method1.parameterNames() != method2.parameterNames()) return false; From 87438fd705b2b81006a18f1c35ebd112da1b3054 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sat, 18 Feb 2012 21:42:19 +0100 Subject: [PATCH 63/74] Move MetaStringTable class to private header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the class to QMetaStringTable and move it to qmetaobjectbuilder_p.h. It must be exported since it will be used by the QtDBus and QtDeclarative meta-object generators. Change-Id: I08d1172fb292ab8f1e891da7f5d5f2798225c77f Reviewed-by: João Abecasis Reviewed-by: Thiago Macieira --- src/corelib/kernel/qmetaobjectbuilder.cpp | 32 +++++++++-------------- src/corelib/kernel/qmetaobjectbuilder_p.h | 18 +++++++++++++ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index ea8fba10d0..994549b8b2 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -1075,26 +1075,18 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name) #define ALIGN(size,type) \ (size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1) -class MetaStringTable -{ -public: - MetaStringTable() : m_index(0) {} +/*! + \class QMetaStringTable + \internal + \brief The QMetaStringTable class can generate a meta-object string table at runtime. +*/ - int enter(const QByteArray &value); - - static int preferredAlignment(); - int blobSize() const; - void writeBlob(char *out); - -private: - typedef QHash Entries; // string --> index mapping - Entries m_entries; - int m_index; -}; +QMetaStringTable::QMetaStringTable() + : m_index(0) {} // Enters the given value into the string table (if it hasn't already been // entered). Returns the index of the string. -int MetaStringTable::enter(const QByteArray &value) +int QMetaStringTable::enter(const QByteArray &value) { Entries::iterator it = m_entries.find(value); if (it != m_entries.end()) @@ -1105,7 +1097,7 @@ int MetaStringTable::enter(const QByteArray &value) return pos; } -int MetaStringTable::preferredAlignment() +int QMetaStringTable::preferredAlignment() { #ifdef Q_ALIGNOF return Q_ALIGNOF(QByteArrayData); @@ -1115,7 +1107,7 @@ int MetaStringTable::preferredAlignment() } // Returns the size (in bytes) required for serializing this string table. -int MetaStringTable::blobSize() const +int QMetaStringTable::blobSize() const { int size = m_entries.size() * sizeof(QByteArrayData); Entries::const_iterator it; @@ -1128,7 +1120,7 @@ int MetaStringTable::blobSize() const // The struct consists of an array of QByteArrayData, followed by a char array // containing the actual strings. This format must match the one produced by // moc (see generator.cpp). -void MetaStringTable::writeBlob(char *out) +void QMetaStringTable::writeBlob(char *out) { Q_ASSERT(!(reinterpret_cast(out) & (preferredAlignment()-1))); @@ -1305,7 +1297,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Reset the current data position to just past the QMetaObjectPrivate. dataIndex = MetaObjectPrivateFieldCount; - MetaStringTable strings; + QMetaStringTable strings; strings.enter(d->className); // Output the class infos, diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index ef802ce82b..4d766a9197 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -56,6 +56,7 @@ #include #include #include +#include #include @@ -319,6 +320,23 @@ private: QMetaEnumBuilderPrivate *d_func() const; }; +class Q_CORE_EXPORT QMetaStringTable +{ +public: + QMetaStringTable(); + + int enter(const QByteArray &value); + + static int preferredAlignment(); + int blobSize() const; + void writeBlob(char *out); + +private: + typedef QHash Entries; // string --> index mapping + Entries m_entries; + int m_index; +}; + Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::AddMembers) Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::MetaObjectFlags) From 019a5f2dd50404abfb06f26d31aa6292eb6344fc Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sat, 18 Feb 2012 22:14:36 +0100 Subject: [PATCH 64/74] Port QDBusMetaObject to new meta-object string format Bring QDBusMetaObject up-to-date with latest moc changes (generating the string table as an array of QByteArrayData). QDBusMetaObject now uses the same string table generator as QMetaObjectBuilder. The Q_CORE_EXPORT for rawStringData() will be removed once QtDBus has been ported to the new meta-method data format (the method name will be stored directly in the standard method descriptor, no need for QtDBus to store it as a separate string). Change-Id: I41165f48501b9b11c0288208cdc770926677a8aa Reviewed-by: Thiago Macieira --- src/corelib/kernel/qmetaobject_p.h | 2 +- src/dbus/qdbusmetaobject.cpp | 49 +++++------------------------- src/dbus/qdbusmetaobject_p.h | 2 +- 3 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 509dede4cb..108d332d61 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -180,7 +180,7 @@ struct QMetaObjectPrivate static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast(metaobject->d.data); } - static const char *rawStringData(const QMetaObject *mo, int index); + Q_CORE_EXPORT static const char *rawStringData(const QMetaObject *mo, int index); static int indexOfSignalRelative(const QMetaObject **baseObject, const char* name, diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index bd7b83bf65..eb2d3dffa8 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -54,6 +54,7 @@ #include "qdbusabstractinterface_p.h" #include +#include #ifndef QT_NO_DBUS @@ -355,36 +356,6 @@ void QDBusMetaObjectGenerator::parseProperties() void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) { - class MetaStringTable - { - public: - typedef QHash Entries; // string --> offset mapping - typedef Entries::const_iterator const_iterator; - Entries::const_iterator constBegin() const - { return m_entries.constBegin(); } - Entries::const_iterator constEnd() const - { return m_entries.constEnd(); } - - MetaStringTable() : m_offset(0) {} - - int enter(const QByteArray &value) - { - Entries::iterator it = m_entries.find(value); - if (it != m_entries.end()) - return it.value(); - int pos = m_offset; - m_entries.insert(value, pos); - m_offset += value.size() + 1; - return pos; - } - - int arraySize() const { return m_offset; } - - private: - Entries m_entries; - int m_offset; - }; - // this code here is mostly copied from qaxbase.cpp // with a few modifications to make it cleaner @@ -397,7 +368,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) idata.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int)); QDBusMetaObjectPrivate *header = reinterpret_cast(idata.data()); - Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 6, "QtDBus meta-object generator should generate the same version as moc"); + Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QtDBus meta-object generator should generate the same version as moc"); header->revision = QMetaObjectPrivate::OutputRevision; header->className = 0; header->classInfoCount = 0; @@ -425,7 +396,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count(); idata.resize(data_size + 1); - MetaStringTable strings; + QMetaStringTable strings; strings.enter(className.toLatin1()); int offset = header->methodData; @@ -484,14 +455,8 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) Q_ASSERT(offset == header->propertyDBusData); Q_ASSERT(signatureOffset == header->methodDBusData); - char *string_data = new char[strings.arraySize()]; - { - MetaStringTable::const_iterator it; - for (it = strings.constBegin(); it != strings.constEnd(); ++it) { - memcpy(string_data + it.value(), it.key().constData(), it.key().size()); - string_data[it.value() + it.key().size()] = '\0'; - } - } + char *string_data = new char[strings.blobSize()]; + strings.writeBlob(string_data); uint *uint_data = new uint[idata.size()]; memcpy(uint_data, idata.data(), idata.size() * sizeof(int)); @@ -499,7 +464,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) // put the metaobject together obj->d.data = uint_data; obj->d.extradata = 0; - obj->d.stringdata = string_data; + obj->d.stringdata = reinterpret_cast(string_data); obj->d.superdata = &QDBusAbstractInterface::staticMetaObject; } @@ -618,7 +583,7 @@ const char *QDBusMetaObject::dbusNameForMethod(int id) const //id -= methodOffset(); if (id >= 0 && id < priv(d.data)->methodCount) { int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return d.stringdata + d.data[handle]; + return QMetaObjectPrivate::rawStringData(this, d.data[handle]); } return 0; } diff --git a/src/dbus/qdbusmetaobject_p.h b/src/dbus/qdbusmetaobject_p.h index 7a8de41fa0..23a7d53016 100644 --- a/src/dbus/qdbusmetaobject_p.h +++ b/src/dbus/qdbusmetaobject_p.h @@ -71,7 +71,7 @@ struct Q_DBUS_EXPORT QDBusMetaObject: public QMetaObject QDBusError &error); ~QDBusMetaObject() { - delete [] d.stringdata; + delete [] reinterpret_cast(d.stringdata); delete [] d.data; } From 3b844c16e0988b017c67b2329cbe34d4da112b33 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sun, 19 Feb 2012 13:25:04 +0100 Subject: [PATCH 65/74] Port QDBusMetaObject to Qt5 meta-property/method descriptor format Adapts QDBusMetaObject to be in sync with the moc/meta-object changes for property and method descriptors (storing the name and argument count of methods, and more elaborate type information). Now that the method name is stored in the standard method descriptor, QtDBus doesn't need to store it separately anymore, and the QMetaObjectPrivate::rawStringData() function can be removed. Change-Id: I04efdbe05b52bbd85405e1713509e55308ac42da Reviewed-by: Thiago Macieira --- src/corelib/kernel/qmetaobject.cpp | 5 - src/corelib/kernel/qmetaobject_p.h | 2 - src/dbus/qdbusinterface.cpp | 2 +- src/dbus/qdbusmetaobject.cpp | 152 ++++++++---- src/dbus/qdbusmetaobject_p.h | 1 - .../qdbusmetaobject/tst_qdbusmetaobject.cpp | 224 ++++++++++++++++++ 6 files changed, 332 insertions(+), 54 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 0a1fb3daf7..b38e7f9004 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -176,11 +176,6 @@ static inline const char *rawStringData(const QMetaObject *mo, int index) return legacyString(mo, index); } -const char *QMetaObjectPrivate::rawStringData(const QMetaObject *mo, int index) -{ - return QT_PREPEND_NAMESPACE(rawStringData)(mo, index); -} - static inline int stringSize(const QMetaObject *mo, int index) { if (priv(mo->d.data)->revision >= 7) diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 108d332d61..ff8dccc4dd 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -180,8 +180,6 @@ struct QMetaObjectPrivate static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast(metaobject->d.data); } - Q_CORE_EXPORT static const char *rawStringData(const QMetaObject *mo, int index); - static int indexOfSignalRelative(const QMetaObject **baseObject, const char* name, bool normalizeStringData); diff --git a/src/dbus/qdbusinterface.cpp b/src/dbus/qdbusinterface.cpp index d390f395ee..b76dd733a2 100644 --- a/src/dbus/qdbusinterface.cpp +++ b/src/dbus/qdbusinterface.cpp @@ -280,7 +280,7 @@ int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv) } else if (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method) { // method call relay from Qt world to D-Bus world // get D-Bus equivalent signature - QString methodName = QLatin1String(metaObject->dbusNameForMethod(id)); + QString methodName = QString::fromLatin1(mm.name()); const int *inputTypes = metaObject->inputTypesForMethod(id); int inputTypesCount = *inputTypes; diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index eb2d3dffa8..7b8a67f343 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -70,7 +70,7 @@ public: private: struct Method { - QByteArray parameters; + QList parameterNames; QByteArray typeName; QByteArray tag; QByteArray name; @@ -104,10 +104,12 @@ private: void parseMethods(); void parseSignals(); void parseProperties(); + + static int aggregateParameterCount(const QMap &map); }; static const int intsPerProperty = 2; -static const int intsPerMethod = 3; +static const int intsPerMethod = 2; struct QDBusMetaObjectPrivate : public QMetaObjectPrivate { @@ -133,6 +135,30 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature, const QDBusIntrospection::Annotations &annotations, const char *direction, int id) { + struct QDBusRawTypeHandler { + static void destroy(void *) + { + qFatal("Cannot destroy placeholder type QDBusRawType"); + } + + static void *create(const void *) + { + qFatal("Cannot create placeholder type QDBusRawType"); + return 0; + } + + static void destruct(void *) + { + qFatal("Cannot destruct placeholder type QDBusRawType"); + } + + static void *construct(void *, const void *) + { + qFatal("Cannot construct placeholder type QDBusRawType"); + return 0; + } + }; + Type result; result.id = QVariant::Invalid; @@ -158,8 +184,13 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature, if (type == QVariant::Invalid || signature != QDBusMetaType::typeToSignature(type)) { // type is still unknown or doesn't match back to the signature that it // was expected to, so synthesize a fake type - type = QMetaType::VoidStar; typeName = "QDBusRawType<0x" + signature.toHex() + ">*"; + type = QMetaType::registerType(typeName, QDBusRawTypeHandler::destroy, + QDBusRawTypeHandler::create, + QDBusRawTypeHandler::destruct, + QDBusRawTypeHandler::construct, + sizeof(void *), + QMetaType::MovableType); } result.name = typeName; @@ -216,8 +247,7 @@ void QDBusMetaObjectGenerator::parseMethods() mm.inputTypes.append(type.id); - mm.parameters.append(arg.name.toLatin1()); - mm.parameters.append(','); + mm.parameterNames.append(arg.name.toLatin1()); prototype.append(type.name); prototype.append(','); @@ -241,8 +271,7 @@ void QDBusMetaObjectGenerator::parseMethods() mm.typeName = type.name; } else { // non-const ref parameter - mm.parameters.append(arg.name.toLatin1()); - mm.parameters.append(','); + mm.parameterNames.append(arg.name.toLatin1()); prototype.append(type.name); prototype.append("&,"); @@ -251,12 +280,10 @@ void QDBusMetaObjectGenerator::parseMethods() if (!ok) continue; // convert the last commas: - if (!mm.parameters.isEmpty()) { - mm.parameters.truncate(mm.parameters.length() - 1); + if (!mm.parameterNames.isEmpty()) prototype[prototype.length() - 1] = ')'; - } else { + else prototype.append(')'); - } // check the async tag if (m.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true")) @@ -296,8 +323,7 @@ void QDBusMetaObjectGenerator::parseSignals() mm.inputTypes.append(type.id); - mm.parameters.append(arg.name.toLatin1()); - mm.parameters.append(','); + mm.parameterNames.append(arg.name.toLatin1()); prototype.append(type.name); prototype.append(','); @@ -305,12 +331,10 @@ void QDBusMetaObjectGenerator::parseSignals() if (!ok) continue; // convert the last commas: - if (!mm.parameters.isEmpty()) { - mm.parameters.truncate(mm.parameters.length() - 1); + if (!mm.parameterNames.isEmpty()) prototype[prototype.length() - 1] = ')'; - } else { + else prototype.append(')'); - } // meta method flags mm.flags = AccessProtected | MethodSignal | MethodScriptable; @@ -343,17 +367,25 @@ void QDBusMetaObjectGenerator::parseProperties() if (p.access != QDBusIntrospection::Property::Read) mp.flags |= Writable; - if (mp.typeName == "QDBusVariant") - mp.flags |= QMetaType::QVariant << 24; - else if (mp.type < 0xff) - // encode the type in the flags - mp.flags |= mp.type << 24; - // add the property: properties.insert(name, mp); } } +// Returns the sum of all parameters (including return type) for the given +// \a map of methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +int QDBusMetaObjectGenerator::aggregateParameterCount(const QMap &map) +{ + int sum = 0; + QMap::const_iterator it; + for (it = map.constBegin(); it != map.constEnd(); ++it) { + const Method &m = it.value(); + sum += m.inputTypes.size() + qMax(1, m.outputTypes.size()); + } + return sum; +} + void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) { // this code here is mostly copied from qaxbase.cpp @@ -367,6 +399,12 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) QVarLengthArray idata; idata.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int)); + int methodParametersDataSize = + ((aggregateParameterCount(signals_) + + aggregateParameterCount(methods)) * 2) // types and parameter names + - signals_.count() // return "parameters" don't have names + - methods.count(); // ditto + QDBusMetaObjectPrivate *header = reinterpret_cast(idata.data()); Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QtDBus meta-object generator should generate the same version as moc"); header->revision = QMetaObjectPrivate::OutputRevision; @@ -376,7 +414,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) header->methodCount = signals_.count() + methods.count(); header->methodData = idata.size(); header->propertyCount = properties.count(); - header->propertyData = header->methodData + header->methodCount * 5; + header->propertyData = header->methodData + header->methodCount * 5 + methodParametersDataSize; header->enumeratorCount = 0; header->enumeratorData = 0; header->constructorCount = 0; @@ -388,7 +426,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) header->methodDBusData = header->propertyDBusData + header->propertyCount * intsPerProperty; int data_size = idata.size() + - (header->methodCount * (5+intsPerMethod)) + + (header->methodCount * (5+intsPerMethod)) + methodParametersDataSize + (header->propertyCount * (3+intsPerProperty)); foreach (const Method &mm, signals_) data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count(); @@ -400,6 +438,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) strings.enter(className.toLatin1()); int offset = header->methodData; + int parametersOffset = offset + header->methodCount * 5; int signatureOffset = header->methodDBusData; int typeidOffset = header->methodDBusData + header->methodCount * intsPerMethod; idata[typeidOffset++] = 0; // eod @@ -410,16 +449,45 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) QMap &map = (x == 0) ? signals_ : methods; for (QMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) { - // form "prototype\0parameters\0typeName\0tag\0methodname\0" const Method &mm = it.value(); - idata[offset++] = strings.enter(it.key()); // prototype - idata[offset++] = strings.enter(mm.parameters); - idata[offset++] = strings.enter(mm.typeName); + int argc = mm.inputTypes.size() + qMax(0, mm.outputTypes.size() - 1); + + idata[offset++] = strings.enter(mm.name); + idata[offset++] = argc; + idata[offset++] = parametersOffset; idata[offset++] = strings.enter(mm.tag); idata[offset++] = mm.flags; - idata[signatureOffset++] = strings.enter(mm.name); + // Parameter types + for (int i = -1; i < argc; ++i) { + int type; + QByteArray typeName; + if (i < 0) { // Return type + if (!mm.outputTypes.isEmpty()) + type = mm.outputTypes.first(); + else + type = QMetaType::Void; + } else if (i < mm.inputTypes.size()) { + type = mm.inputTypes.at(i); + } else { + Q_ASSERT(mm.outputTypes.size() > 1); + type = mm.outputTypes.at(i - mm.inputTypes.size() + 1); + // Output parameters are references; type id not available + typeName = QMetaType::typeName(type); + typeName.append('&'); + } + Q_ASSERT(type || (i < 0)); + int typeInfo; + if (!typeName.isEmpty()) + typeInfo = IsUnresolvedType | strings.enter(typeName); + else + typeInfo = type; + idata[parametersOffset++] = typeInfo; + } + // Parameter names + for (int i = 0; i < argc; ++i) + idata[parametersOffset++] = strings.enter(mm.parameterNames.at(i)); idata[signatureOffset++] = typeidOffset; idata[typeidOffset++] = mm.inputTypes.count(); @@ -433,9 +501,12 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) } } - Q_ASSERT(offset == header->propertyData); + Q_ASSERT(offset == header->methodData + header->methodCount * 5); + Q_ASSERT(parametersOffset = header->propertyData); Q_ASSERT(signatureOffset == header->methodDBusData + header->methodCount * intsPerMethod); Q_ASSERT(typeidOffset == idata.size()); + offset += methodParametersDataSize; + Q_ASSERT(offset == header->propertyData); // add each property signatureOffset = header->propertyDBusData; @@ -443,9 +514,10 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) it != properties.constEnd(); ++it) { const Property &mp = it.value(); - // form is "name\0typeName\0signature\0" + // form is name, typeinfo, flags idata[offset++] = strings.enter(it.key()); // name - idata[offset++] = strings.enter(mp.typeName); + Q_ASSERT(mp.type != 0); + idata[offset++] = mp.type; idata[offset++] = mp.flags; idata[signatureOffset++] = strings.enter(mp.signature); @@ -578,22 +650,12 @@ static inline const QDBusMetaObjectPrivate *priv(const uint* data) return reinterpret_cast(data); } -const char *QDBusMetaObject::dbusNameForMethod(int id) const -{ - //id -= methodOffset(); - if (id >= 0 && id < priv(d.data)->methodCount) { - int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return QMetaObjectPrivate::rawStringData(this, d.data[handle]); - } - return 0; -} - const int *QDBusMetaObject::inputTypesForMethod(int id) const { //id -= methodOffset(); if (id >= 0 && id < priv(d.data)->methodCount) { int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return reinterpret_cast(d.data + d.data[handle + 1]); + return reinterpret_cast(d.data + d.data[handle]); } return 0; } @@ -603,7 +665,7 @@ const int *QDBusMetaObject::outputTypesForMethod(int id) const //id -= methodOffset(); if (id >= 0 && id < priv(d.data)->methodCount) { int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return reinterpret_cast(d.data + d.data[handle + 2]); + return reinterpret_cast(d.data + d.data[handle + 1]); } return 0; } diff --git a/src/dbus/qdbusmetaobject_p.h b/src/dbus/qdbusmetaobject_p.h index 23a7d53016..98d6105c72 100644 --- a/src/dbus/qdbusmetaobject_p.h +++ b/src/dbus/qdbusmetaobject_p.h @@ -76,7 +76,6 @@ struct Q_DBUS_EXPORT QDBusMetaObject: public QMetaObject } // methods (slots & signals): - const char *dbusNameForMethod(int id) const; const int *inputTypesForMethod(int id) const; const int *outputTypesForMethod(int id) const; diff --git a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp index f705fe474c..a523a66bdd 100644 --- a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp +++ b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp @@ -403,10 +403,15 @@ void tst_QDBusMetaObject::types() QCOMPARE(int(constructed.access()), int(expected.access())); QCOMPARE(int(constructed.methodType()), int(expected.methodType())); + QCOMPARE(constructed.name(), expected.name()); + QCOMPARE(constructed.parameterCount(), expected.parameterCount()); QCOMPARE(constructed.parameterNames(), expected.parameterNames()); QCOMPARE(constructed.parameterTypes(), expected.parameterTypes()); + for (int j = 0; j < constructed.parameterCount(); ++j) + QCOMPARE(constructed.parameterType(j), expected.parameterType(j)); QCOMPARE(constructed.tag(), expected.tag()); QCOMPARE(constructed.typeName(), expected.typeName()); + QCOMPARE(constructed.returnType(), expected.returnType()); } for (int i = metaobject->propertyOffset(); i < metaobject->propertyCount(); ++i) { @@ -427,6 +432,8 @@ void tst_QDBusMetaObject::types() QCOMPARE(constructed.isUser(), expected.isUser()); QCOMPARE(constructed.isWritable(), expected.isWritable()); QCOMPARE(constructed.typeName(), expected.typeName()); + QCOMPARE(constructed.type(), expected.type()); + QCOMPARE(constructed.userType(), expected.userType()); } } @@ -667,6 +674,204 @@ const char PropertyTest4_xml[] = "" ""; +class PropertyTest_b: public QObject +{ + Q_OBJECT + Q_PROPERTY(bool property READ property WRITE setProperty) +public: + bool property() { return false; } + void setProperty(bool) { } +}; +const char PropertyTest_b_xml[] = + ""; + +class PropertyTest_y: public QObject +{ + Q_OBJECT + Q_PROPERTY(uchar property READ property WRITE setProperty) +public: + uchar property() { return 0; } + void setProperty(uchar) { } +}; +const char PropertyTest_y_xml[] = + ""; + +class PropertyTest_n: public QObject +{ + Q_OBJECT + Q_PROPERTY(short property READ property WRITE setProperty) +public: + short property() { return 0; } + void setProperty(short) { } +}; +const char PropertyTest_n_xml[] = + ""; + +class PropertyTest_q: public QObject +{ + Q_OBJECT + Q_PROPERTY(ushort property READ property WRITE setProperty) +public: + ushort property() { return 0; } + void setProperty(ushort) { } +}; +const char PropertyTest_q_xml[] = + ""; + +class PropertyTest_u: public QObject +{ + Q_OBJECT + Q_PROPERTY(uint property READ property WRITE setProperty) +public: + uint property() { return 0; } + void setProperty(uint) { } +}; +const char PropertyTest_u_xml[] = + ""; + +class PropertyTest_x: public QObject +{ + Q_OBJECT + Q_PROPERTY(qlonglong property READ property WRITE setProperty) +public: + qlonglong property() { return 0; } + void setProperty(qlonglong) { } +}; +const char PropertyTest_x_xml[] = + ""; + +class PropertyTest_t: public QObject +{ + Q_OBJECT + Q_PROPERTY(qulonglong property READ property WRITE setProperty) +public: + qulonglong property() { return 0; } + void setProperty(qulonglong) { } +}; +const char PropertyTest_t_xml[] = + ""; + +class PropertyTest_d: public QObject +{ + Q_OBJECT + Q_PROPERTY(double property READ property WRITE setProperty) +public: + double property() { return 0; } + void setProperty(double) { } +}; +const char PropertyTest_d_xml[] = + ""; + +class PropertyTest_s: public QObject +{ + Q_OBJECT + Q_PROPERTY(QString property READ property WRITE setProperty) +public: + QString property() { return QString(); } + void setProperty(QString) { } +}; +const char PropertyTest_s_xml[] = + ""; + +class PropertyTest_v: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusVariant property READ property WRITE setProperty) +public: + QDBusVariant property() { return QDBusVariant(); } + void setProperty(QDBusVariant) { } +}; +const char PropertyTest_v_xml[] = + ""; + +class PropertyTest_o: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusObjectPath property READ property WRITE setProperty) +public: + QDBusObjectPath property() { return QDBusObjectPath(); } + void setProperty(QDBusObjectPath) { } +}; +const char PropertyTest_o_xml[] = + ""; + +class PropertyTest_g: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusSignature property READ property WRITE setProperty) +public: + QDBusSignature property() { return QDBusSignature(); } + void setProperty(QDBusSignature) { } +}; +const char PropertyTest_g_xml[] = + ""; + +class PropertyTest_h: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusUnixFileDescriptor property READ property WRITE setProperty) +public: + QDBusUnixFileDescriptor property() { return QDBusUnixFileDescriptor(); } + void setProperty(QDBusUnixFileDescriptor) { } +}; +const char PropertyTest_h_xml[] = + ""; + +class PropertyTest_ay: public QObject +{ + Q_OBJECT + Q_PROPERTY(QByteArray property READ property WRITE setProperty) +public: + QByteArray property() { return QByteArray(); } + void setProperty(QByteArray) { } +}; +const char PropertyTest_ay_xml[] = + ""; + +class PropertyTest_as: public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList property READ property WRITE setProperty) +public: + QStringList property() { return QStringList(); } + void setProperty(QStringList) { } +}; +const char PropertyTest_as_xml[] = + ""; + +class PropertyTest_av: public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariantList property READ property WRITE setProperty) +public: + QVariantList property() { return QVariantList(); } + void setProperty(QVariantList) { } +}; +const char PropertyTest_av_xml[] = + ""; + +class PropertyTest_ao: public QObject +{ + Q_OBJECT + Q_PROPERTY(QList property READ property WRITE setProperty) +public: + QList property() { return QList(); } + void setProperty(QList) { } +}; +const char PropertyTest_ao_xml[] = + ""; + +class PropertyTest_ag: public QObject +{ + Q_OBJECT + Q_PROPERTY(QList property READ property WRITE setProperty) +public: + QList property() { return QList(); } + void setProperty(QList) { } +}; +const char PropertyTest_ag_xml[] = + ""; + void tst_QDBusMetaObject::properties_data() { QTest::addColumn("metaobject"); @@ -676,6 +881,25 @@ void tst_QDBusMetaObject::properties_data() QTest::newRow("readwrite") << &PropertyTest2::staticMetaObject << QString(PropertyTest2_xml); QTest::newRow("write") << &PropertyTest3::staticMetaObject << QString(PropertyTest3_xml); QTest::newRow("customtype") << &PropertyTest4::staticMetaObject << QString(PropertyTest4_xml); + + QTest::newRow("bool") << &PropertyTest_b::staticMetaObject << QString(PropertyTest_b_xml); + QTest::newRow("byte") << &PropertyTest_y::staticMetaObject << QString(PropertyTest_y_xml); + QTest::newRow("short") << &PropertyTest_n::staticMetaObject << QString(PropertyTest_n_xml); + QTest::newRow("ushort") << &PropertyTest_q::staticMetaObject << QString(PropertyTest_q_xml); + QTest::newRow("uint") << &PropertyTest_u::staticMetaObject << QString(PropertyTest_u_xml); + QTest::newRow("qlonglong") << &PropertyTest_x::staticMetaObject << QString(PropertyTest_x_xml); + QTest::newRow("qulonglong") << &PropertyTest_t::staticMetaObject << QString(PropertyTest_t_xml); + QTest::newRow("double") << &PropertyTest_d::staticMetaObject << QString(PropertyTest_d_xml); + QTest::newRow("QString") << &PropertyTest_s::staticMetaObject << QString(PropertyTest_s_xml); + QTest::newRow("QDBusVariant") << &PropertyTest_v::staticMetaObject << QString(PropertyTest_v_xml); + QTest::newRow("QDBusObjectPath") << &PropertyTest_o::staticMetaObject << QString(PropertyTest_o_xml); + QTest::newRow("QDBusSignature") << &PropertyTest_g::staticMetaObject << QString(PropertyTest_g_xml); + QTest::newRow("QDBusUnixFileDescriptor") << &PropertyTest_h::staticMetaObject << QString(PropertyTest_h_xml); + QTest::newRow("QByteArray") << &PropertyTest_ay::staticMetaObject << QString(PropertyTest_ay_xml); + QTest::newRow("QStringList") << &PropertyTest_as::staticMetaObject << QString(PropertyTest_as_xml); + QTest::newRow("QVariantList") << &PropertyTest_av::staticMetaObject << QString(PropertyTest_av_xml); + QTest::newRow("QList") << &PropertyTest_ao::staticMetaObject << QString(PropertyTest_ao_xml); + QTest::newRow("QList") << &PropertyTest_ag::staticMetaObject << QString(PropertyTest_ag_xml); } void tst_QDBusMetaObject::properties() From dc6b8112e3f7a36a06b163224cddf2122ca2c0fa Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 21 Feb 2012 19:36:18 +0100 Subject: [PATCH 66/74] Update QSignalEventGenerator to meta-object revision 7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate the moc output so that it's in sync with the latest moc. Change-Id: Ic347f7cd030248da431070bf3307950df30edd66 Reviewed-by: Thiago Macieira Reviewed-by: Jędrzej Nowacki --- src/corelib/statemachine/qstatemachine.cpp | 36 ++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 3992c4060e..be96d895a2 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -2211,10 +2211,29 @@ void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation) // Begin moc-generated code -- modify carefully (check "HAND EDIT" parts)! +struct qt_meta_stringdata_QSignalEventGenerator_t { + QByteArrayData data[3]; + char stringdata[32]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) { \ + Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ + offsetof(qt_meta_stringdata_QSignalEventGenerator_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + } +static const qt_meta_stringdata_QSignalEventGenerator_t qt_meta_stringdata_QSignalEventGenerator = { + { +QT_MOC_LITERAL(0, 0, 21), +QT_MOC_LITERAL(1, 22, 7), +QT_MOC_LITERAL(2, 30, 0) + }, + "QSignalEventGenerator\0execute\0\0" +}; +#undef QT_MOC_LITERAL + static const uint qt_meta_data_QSignalEventGenerator[] = { // content: - 6, // revision + 7, // revision 0, // classname 0, 0, // classinfo 1, 14, // methods @@ -2224,16 +2243,15 @@ static const uint qt_meta_data_QSignalEventGenerator[] = { 0, // flags 0, // signalCount - // slots: signature, parameters, type, tag, flags - 23, 22, 22, 22, 0x0a, + // slots: name, argc, parameters, tag, flags + 1, 0, 19, 2, 0x0a, + + // slots: parameters + QMetaType::Void, 0 // eod }; -static const char qt_meta_stringdata_QSignalEventGenerator[] = { - "QSignalEventGenerator\0\0execute()\0" -}; - void QSignalEventGenerator::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { @@ -2252,7 +2270,7 @@ const QMetaObjectExtraData QSignalEventGenerator::staticMetaObjectExtraData = { }; const QMetaObject QSignalEventGenerator::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator, + { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator.data, qt_meta_data_QSignalEventGenerator, &staticMetaObjectExtraData } }; @@ -2264,7 +2282,7 @@ const QMetaObject *QSignalEventGenerator::metaObject() const void *QSignalEventGenerator::qt_metacast(const char *_clname) { if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator)) + if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator.stringdata)) return static_cast(const_cast< QSignalEventGenerator*>(this)); return QObject::qt_metacast(_clname); } From 4a28fecd36ea63b401515287ee6865ac077d9e4b Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Sun, 19 Feb 2012 13:09:24 +0100 Subject: [PATCH 67/74] Port QMetaObjectBuilder to Qt5 meta-property/method descriptor format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adapts QMOB to be in sync with the moc/meta-object changes for property and method descriptors (storing the name and argument count of methods, and more elaborate type information). Change-Id: Ia32a115643e99e39d76e46a9171219cbba522233 Reviewed-by: João Abecasis --- src/corelib/kernel/qmetaobjectbuilder.cpp | 165 ++++++++++++---------- 1 file changed, 88 insertions(+), 77 deletions(-) diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 994549b8b2..13cd1a684a 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -78,26 +78,17 @@ QT_BEGIN_NAMESPACE */ // copied from moc's generator.cpp -uint qvariant_nameToType(const char* name) +bool isBuiltinType(const QByteArray &type) { - if (!name) - return 0; - - uint tp = QMetaType::type(name); - return tp < QMetaType::User ? tp : 0; -} - -/* - Returns true if the type is a QVariant types. -*/ -bool isVariantType(const char* type) -{ - return qvariant_nameToType(type) != 0; + int id = QMetaType::type(type); + if (!id && !type.isEmpty() && type != "void") + return false; + return (id < QMetaType::User); } +// copied from qmetaobject.cpp static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast(data); } -// end of copied lines from qmetaobject.cpp class QMetaMethodBuilderPrivate { @@ -141,6 +132,16 @@ public: { return QMetaObjectPrivate::parameterTypeNamesFromSignature(signature); } + + int parameterCount() const + { + return parameterTypes().size(); + } + + QByteArray name() const + { + return signature.left(qMax(signature.indexOf('('), 0)); + } }; class QMetaPropertyBuilderPrivate @@ -1142,45 +1143,15 @@ void QMetaStringTable::writeBlob(char *out) } } -// Build the parameter array string for a method. -static QByteArray buildParameterNames - (const QByteArray& signature, const QList& parameterNames) +// Returns the sum of all parameters (including return type) for the given +// \a methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +static int aggregateParameterCount(const QList &methods) { - // If the parameter name list is specified, then concatenate them. - if (!parameterNames.isEmpty()) { - QByteArray names; - bool first = true; - foreach (const QByteArray &name, parameterNames) { - if (first) - first = false; - else - names += (char)','; - names += name; - } - return names; - } - - // Count commas in the signature, excluding those inside template arguments. - int index = signature.indexOf('('); - if (index < 0) - return QByteArray(); - ++index; - if (index >= signature.size()) - return QByteArray(); - if (signature[index] == ')') - return QByteArray(); - int count = 1; - int brackets = 0; - while (index < signature.size() && signature[index] != ',') { - char ch = signature[index++]; - if (ch == '<') - ++brackets; - else if (ch == '>') - --brackets; - else if (ch == ',' && brackets <= 0) - ++count; - } - return QByteArray(count - 1, ','); + int sum = 0; + for (int i = 0; i < methods.size(); ++i) + sum += methods.at(i).parameterCount() + 1; // +1 for return type + return sum; } // Build a QMetaObject in "buf" based on the information in "d". @@ -1193,6 +1164,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_UNUSED(expectedSize); // Avoid warning in release mode int size = 0; int dataIndex; + int paramsIndex; int enumIndex; int index; bool hasRevisionedMethods = d->hasRevisionedMethods(); @@ -1223,6 +1195,11 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, break; } } + int methodParametersDataSize = + ((aggregateParameterCount(d->methods) + + aggregateParameterCount(d->constructors)) * 2) // types and parameter names + - d->methods.size() // return "parameters" don't have names + - d->constructors.size(); // "this" parameters don't have names if (buf) { Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; @@ -1239,6 +1216,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5 * d->methods.size(); if (hasRevisionedMethods) dataIndex += d->methods.size(); + paramsIndex = dataIndex; + dataIndex += methodParametersDataSize; pmeta->propertyCount = d->properties.size(); pmeta->propertyData = dataIndex; @@ -1260,6 +1239,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5 * d->methods.size(); if (hasRevisionedMethods) dataIndex += d->methods.size(); + paramsIndex = dataIndex; + dataIndex += methodParametersDataSize; dataIndex += 3 * d->properties.size(); if (hasNotifySignals) dataIndex += d->properties.size(); @@ -1316,24 +1297,21 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->methodData); for (index = 0; index < d->methods.size(); ++index) { QMetaMethodBuilderPrivate *method = &(d->methods[index]); - int sig = strings.enter(method->signature); - int params; - QByteArray names = buildParameterNames - (method->signature, method->parameterNames); - params = strings.enter(names); - int ret = strings.enter(method->returnType); + int name = strings.enter(method->name()); + int argc = method->parameterCount(); int tag = strings.enter(method->tag); int attrs = method->attributes; if (buf) { - data[dataIndex] = sig; - data[dataIndex + 1] = params; - data[dataIndex + 2] = ret; + data[dataIndex] = name; + data[dataIndex + 1] = argc; + data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; if (method->methodType() == QMetaMethod::Signal) pmeta->signalCount++; } dataIndex += 5; + paramsIndex += 1 + argc * 2; } if (hasRevisionedMethods) { for (index = 0; index < d->methods.size(); ++index) { @@ -1344,23 +1322,59 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, } } + // Output the method parameters in the class. + Q_ASSERT(!buf || dataIndex == pmeta->methodData + d->methods.size() * 5 + + (hasRevisionedMethods ? d->methods.size() : 0)); + for (int x = 0; x < 2; ++x) { + QList &methods = (x == 0) ? d->methods : d->constructors; + for (index = 0; index < methods.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(methods[index]); + QList paramTypeNames = method->parameterTypes(); + int paramCount = paramTypeNames.size(); + for (int i = -1; i < paramCount; ++i) { + const QByteArray &typeName = (i < 0) ? method->returnType : paramTypeNames.at(i); + int typeInfo; + if (isBuiltinType(typeName)) + typeInfo = QMetaType::type(typeName); + else + typeInfo = IsUnresolvedType | strings.enter(typeName); + if (buf) + data[dataIndex] = typeInfo; + ++dataIndex; + } + + QList paramNames = method->parameterNames; + while (paramNames.size() < paramCount) + paramNames.append(QByteArray()); + for (int i = 0; i < paramCount; ++i) { + int stringIndex = strings.enter(paramNames.at(i)); + if (buf) + data[dataIndex] = stringIndex; + ++dataIndex; + } + } + } + // Output the properties in the class. Q_ASSERT(!buf || dataIndex == pmeta->propertyData); for (index = 0; index < d->properties.size(); ++index) { QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); int name = strings.enter(prop->name); - int type = strings.enter(prop->type); + + int typeInfo; + if (isBuiltinType(prop->type)) + typeInfo = QMetaType::type(prop->type); + else + typeInfo = IsUnresolvedType | strings.enter(prop->type); + int flags = prop->flags; - if (!isVariantType(prop->type)) { + if (!isBuiltinType(prop->type)) flags |= EnumOrFlag; - } else { - flags |= qvariant_nameToType(prop->type) << 24; - } if (buf) { data[dataIndex] = name; - data[dataIndex + 1] = type; + data[dataIndex + 1] = typeInfo; data[dataIndex + 2] = flags; } dataIndex += 3; @@ -1415,22 +1429,19 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->constructorData); for (index = 0; index < d->constructors.size(); ++index) { QMetaMethodBuilderPrivate *method = &(d->constructors[index]); - int sig = strings.enter(method->signature); - int params; - QByteArray names = buildParameterNames - (method->signature, method->parameterNames); - params = strings.enter(names); - int ret = strings.enter(method->returnType); + int name = strings.enter(method->name()); + int argc = method->parameterCount(); int tag = strings.enter(method->tag); int attrs = method->attributes; if (buf) { - data[dataIndex] = sig; - data[dataIndex + 1] = params; - data[dataIndex + 2] = ret; + data[dataIndex] = name; + data[dataIndex + 1] = argc; + data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; } dataIndex += 5; + paramsIndex += 1 + argc * 2; } size += strings.blobSize(); From f94709366229b479a17457021cff0d664cfe8de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Fri, 17 Feb 2012 01:14:08 +0100 Subject: [PATCH 68/74] Test setSharable with "raw data" Change-Id: I91774685e84416407aa1fa136f27fedb82545a12 Reviewed-by: Thiago Macieira --- tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 0112d714f6..36bf7516a9 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1104,6 +1104,8 @@ void tst_QArrayData::setSharable_data() QArrayData::CapacityReserved)); QArrayDataPointer staticArray( static_cast *>(&staticArrayData.header)); + QArrayDataPointer rawData( + QTypedArrayData::fromRawData(staticArrayData.data, 10)); nonEmpty->copyAppend(5, 1); nonEmptyReserved->copyAppend(7, 2); @@ -1115,6 +1117,7 @@ void tst_QArrayData::setSharable_data() 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; + QTest::newRow("raw-data") << rawData << size_t(10) << size_t(0) << false << 3; } void tst_QArrayData::setSharable() From 7919c0529ed5bdafd9a7dab82780ad1b1bf33178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 16 Feb 2012 23:25:33 +0100 Subject: [PATCH 69/74] Add AllocationOption::Grow This is meant to reduce the number of allocations on growing containers. It serves the same purpose as the existing qAllocMore which is currently used by container classes. While only a container knows when it is growing, it doesn't need to care how that information is used. qAllocMore is currently treated as a black-box and its result is (basically) forwarded blindly to an allocate function. In that respect, container code using qAllocMore acts as an intermediary. By merging that functionality in the allocate function itself we offer the same benefits without the intermediaries, allowing for simpler code and centralized decisions on memory allocation. Once all users of qAllocMore get ported to QArrayData and QArrayData::allocate, qAllocMore can be moved or more closely integrated into qarraydata.cpp and qtools_p.h can be dropped. Change-Id: I4c09bf7df274b45c399082fc7113a18e4641c5f0 Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qarraydata.cpp | 11 ++++-- src/corelib/tools/qarraydata.h | 7 ++-- .../corelib/tools/qarraydata/simplevector.h | 6 ++-- .../tools/qarraydata/tst_qarraydata.cpp | 34 ++++++++++++++++++- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 5ed3ce015f..8498d0e4d5 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include +#include #include @@ -63,14 +64,20 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, ? const_cast(&qt_array_empty) : const_cast(&qt_array_unsharable_empty); - size_t allocSize = sizeof(QArrayData) + objectSize * capacity; + size_t headerSize = sizeof(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! // Padding is skipped when allocating a header for RawData. if (!(options & RawData)) - allocSize += (alignment - Q_ALIGNOF(QArrayData)); + headerSize += (alignment - Q_ALIGNOF(QArrayData)); + + // Allocate additional space if array is growing + if (options & Grow) + capacity = qAllocMore(objectSize * capacity, headerSize) / objectSize; + + size_t allocSize = headerSize + objectSize * capacity; QArrayData *header = static_cast(::malloc(allocSize)); if (header) { diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index da2058dccf..351a75aade 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -81,9 +81,10 @@ struct Q_CORE_EXPORT QArrayData } enum AllocationOption { - CapacityReserved = 0x1, - Unsharable = 0x2, - RawData = 0x4, + CapacityReserved = 0x1, + Unsharable = 0x2, + RawData = 0x4, + Grow = 0x8, Default = 0 }; diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index a0a9b5f764..fe8108bff2 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -178,7 +178,7 @@ public: || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), - d->detachFlags())); + d->detachFlags() | Data::Grow)); detached.d->copyAppend(first, last); detached.d->copyAppend(begin, begin + d->size); @@ -199,7 +199,7 @@ public: || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), - d->detachFlags())); + d->detachFlags() | Data::Grow)); if (d->size) { const T *const begin = constBegin(); @@ -239,7 +239,7 @@ public: || capacity() - size() < size_t(last - first)) { SimpleVector detached(Data::allocate( qMax(capacity(), size() + (last - first)), - d->detachFlags())); + d->detachFlags() | Data::Grow)); if (position) detached.d->copyAppend(begin, where); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 36bf7516a9..f3f1daba0f 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -87,6 +87,7 @@ private slots: void literals(); void variadicLiterals(); void rValueReferences(); + void grow(); }; template const T &const_(const T &t) { return t; } @@ -651,6 +652,7 @@ void tst_QArrayData::allocate_data() QArrayData::CapacityReserved | QArrayData::Unsharable, true, false, unsharable_empty }, { "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty }, + { "Grow", QArrayData::Grow, false, true, shared_empty } }; for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) @@ -690,7 +692,10 @@ void tst_QArrayData::allocate() keeper.headers.append(data); QCOMPARE(data->size, 0); - QVERIFY(data->alloc >= uint(capacity)); + if (allocateOptions & QArrayData::Grow) + QVERIFY(data->alloc > uint(capacity)); + else + QCOMPARE(data->alloc, uint(capacity)); QCOMPARE(data->capacityReserved, uint(isCapacityReserved)); QCOMPARE(data->ref.isSharable(), isSharable); @@ -1385,5 +1390,32 @@ void tst_QArrayData::rValueReferences() #endif } +void tst_QArrayData::grow() +{ + SimpleVector vector; + + QCOMPARE(vector.size(), size_t(0)); + + size_t previousCapacity = vector.capacity(); + size_t allocations = 0; + for (size_t i = 1; i <= (1 << 20); ++i) { + int source[1] = { i }; + vector.append(source, source + 1); + QCOMPARE(vector.size(), i); + if (vector.capacity() != previousCapacity) { + previousCapacity = vector.capacity(); + ++allocations; + } + } + QCOMPARE(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 < (1 << 20); ++i) + QCOMPARE(const_(vector).at(i), int(i + 1)); +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" From 737c0a3717a5a52037fe18d664a93d5f6f52a1bc Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 26 Feb 2012 21:51:17 +0100 Subject: [PATCH 70/74] QVector: fix initializer_list constructor implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old implementation didn't compile. Change-Id: I9892e1fff11b3a03607c468c9091eebea7e62584 Reviewed-by: Olivier Goffart Reviewed-by: João Abecasis --- src/corelib/tools/qvector.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 8c686a2547..c119ef43ae 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -452,9 +452,17 @@ QVector::QVector(std::initializer_list args) d->alloc = uint(d->size); d->capacityReserved = false; d->offset = offsetOfTypedData(); - T* i = d->end(); - auto it = args.end(); - while (i != d->begin()) + if (QTypeInfo::isComplex) { + T* b = d->begin(); + T* i = d->end(); + const T* s = args.end(); + while (i != b) + new(--i) T(*--s); + } else { + // std::initializer_list::iterator is guaranteed to be + // const T* ([support.initlist]/1), so can be memcpy'ed away from: + ::memcpy(d->begin(), args.begin(), args.size() * sizeof(T)); + } } #endif From 2b70a7d25c3e73d02d6d14075790668dcfc16e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 21 Feb 2012 14:48:47 +0100 Subject: [PATCH 71/74] QList: have operator= defer to copy-ctor and swap Change-Id: I0f9bdbc444abfaea35278281b6c1dff4b52c526f Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qlist.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 08dedb4e94..798351cd61 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -419,13 +419,8 @@ template Q_INLINE_TEMPLATE QList &QList::operator=(const QList &l) { if (d != l.d) { - QListData::Data *o = l.d; - o->ref.ref(); - if (!d->ref.deref()) - dealloc(d); - d = o; - if (!d->sharable) - detach_helper(); + QList tmp(l); + tmp.swap(*this); } return *this; } From 362bde8e8eef41fc6a338ed8fbe6cbf7e9996019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 13 Feb 2012 16:26:35 +0100 Subject: [PATCH 72/74] Introduce QMetaType::UnknownType. QMetaType::Void was ambiguous, it was pointing to a valid type (void) and in the same time it was signaling errors in QMetaType. There was no clean way to check if returned type was valid void or some unregistered type. This feature will be used by new QMetaObject revision which will store type ids instead of type names. So it will be easy to distinguish between: void mySlot(); MyUnregisteredType mySlot(); Change-Id: I73ff097f75585a95e12df74d50c6f3141153e771 Reviewed-by: Kent Hansen Reviewed-by: Olivier Goffart --- dist/changes-5.0.0 | 14 +++++ .../code/src_corelib_kernel_qmetatype.cpp | 2 +- src/corelib/kernel/qmetaobject.cpp | 36 +++++------ src/corelib/kernel/qmetatype.cpp | 62 +++++++++++-------- src/corelib/kernel/qmetatype.h | 9 +-- src/corelib/kernel/qmetatypeswitcher_p.h | 7 ++- src/corelib/kernel/qvariant.cpp | 2 +- src/corelib/kernel/qvariant.h | 2 +- src/corelib/kernel/qvariant_p.h | 29 +++++++-- src/dbus/qdbusintegrator.cpp | 4 +- src/dbus/qdbusmetaobject.cpp | 10 +-- src/dbus/qdbusmetatype.cpp | 4 +- src/dbus/qdbusmisc.cpp | 2 +- src/testlib/qsignaldumper.cpp | 3 +- src/testlib/qsignalspy.h | 4 +- src/tools/moc/generator.cpp | 20 +++--- src/tools/moc/moc.cpp | 4 +- .../kernel/qmetamethod/tst_qmetamethod.cpp | 17 +++-- .../kernel/qmetatype/tst_qmetatype.cpp | 4 ++ .../corelib/kernel/qvariant/tst_qvariant.cpp | 12 +++- 20 files changed, 152 insertions(+), 95 deletions(-) diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index cb1f8f4528..95dbe0b1d7 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -516,6 +516,20 @@ Qt for Windows CE QMetaType::User, which means that it points to the first registered custom type, instead of a nonexistent type. +- QMetaType + + * Interpretation of QMetaType::Void was changed. Before, in some cases + it was returned as an invalid type id, but sometimes it was used as a valid + type (C++ "void"). In Qt5, new QMetaType::UnknownType was introduced to + distinguish between these two. QMetaType::UnknownType is an invalid type id + signaling that a type is unknown to QMetaType, and QMetaType::Void + is a valid type id of C++ void type. The difference will be visible for + example in call to QMetaType::typeName(), this function will return null for + QMetaType::UnknownType and a pointer to "void" string for + QMetaType::Void. + Please, notice that QMetaType::UnknownType has value 0, which previously was + reserved for QMetaType::Void. + - QMessageBox diff --git a/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp b/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp index 9d72c42504..d0a7a69884 100644 --- a/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp +++ b/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp @@ -73,7 +73,7 @@ MyStruct s2 = var.value(); //! [3] int id = QMetaType::type("MyClass"); -if (id != 0) { +if (id != QMetaType::UnknownType) { void *myClassPtr = QMetaType::create(id); ... QMetaType::destroy(id, myClassPtr); diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index b38e7f9004..e446d8b6ba 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1792,14 +1792,14 @@ QByteArray QMetaMethod::name() const Returns the return type of this method. The return value is one of the types that are registered - with QMetaType, or 0 if the type is not registered. + with QMetaType, or QMetaType::UnknownType if the type is not registered. \sa parameterType(), QMetaType, typeName() */ int QMetaMethod::returnType() const { if (!mobj) - return 0; + return QMetaType::UnknownType; return QMetaMethodPrivate::get(this)->returnType(); } @@ -1823,16 +1823,16 @@ int QMetaMethod::parameterCount() const Returns the type of the parameter at the given \a index. The return value is one of the types that are registered - with QMetaType, or 0 if the type is not registered. + with QMetaType, or QMetaType::UnknownType if the type is not registered. \sa parameterCount(), returnType(), QMetaType */ int QMetaMethod::parameterType(int index) const { if (!mobj || index < 0) - return 0; + return QMetaType::UnknownType; if (index >= QMetaMethodPrivate::get(this)->parameterCount()) - return 0; + return QMetaType::UnknownType; return QMetaMethodPrivate::get(this)->parameterType(index); } @@ -2241,7 +2241,7 @@ bool QMetaMethod::invoke(QObject *object, for (int i = 1; i < paramCount; ++i) { types[i] = QMetaType::type(typeNames[i]); - if (types[i]) { + if (types[i] != QMetaType::UnknownType) { args[i] = QMetaType::create(types[i], param[i]); ++nargs; } else if (param[i]) { @@ -2715,11 +2715,11 @@ QVariant::Type QMetaProperty::type() const uint flags = mobj->d.data[handle + 2]; type = flags >> 24; } - if (type) + if (type != QMetaType::UnknownType) return QVariant::Type(type); if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId == 0) + if (enumMetaTypeId == QMetaType::UnknownType) return QVariant::Int; } #ifdef QT_COORD_TYPE @@ -2735,7 +2735,7 @@ QVariant::Type QMetaProperty::type() const \since 4.2 Returns this property's user type. The return value is one - of the values that are registered with QMetaType, or 0 if + of the values that are registered with QMetaType, or QMetaType::UnknownType if the type is not registered. \sa type(), QMetaType, typeName() @@ -2743,11 +2743,11 @@ QVariant::Type QMetaProperty::type() const int QMetaProperty::userType() const { if (!mobj) - return 0; + return QMetaType::UnknownType; if (priv(mobj->d.data)->revision >= 7) { int handle = priv(mobj->d.data)->propertyData + 3*idx; int type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); - if (type) + if (type != QMetaType::UnknownType) return type; } else { QVariant::Type tp = type(); @@ -2756,7 +2756,7 @@ int QMetaProperty::userType() const } if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId == 0) + if (enumMetaTypeId == QMetaType::UnknownType) return QVariant::Int; // Match behavior of QMetaType::type() return enumMetaTypeId; } @@ -2853,7 +2853,7 @@ QVariant QMetaProperty::read(const QObject *object) const with QMetaType) */ int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId != 0) + if (enumMetaTypeId != QMetaType::UnknownType) t = enumMetaTypeId; } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; @@ -2869,14 +2869,14 @@ QVariant QMetaProperty::read(const QObject *object) const } else { uint flags = mobj->d.data[handle + 2]; t = (flags >> 24); - if (t == QVariant::Invalid) { + if (t == QMetaType::UnknownType) { typeName = legacyString(mobj, mobj->d.data[handle + 1]); t = QMetaType::type(typeName); - if (t == QVariant::Invalid) + if (t == QMetaType::UnknownType) t = QVariant::nameToType(typeName); } } - if (t == QVariant::Invalid) { + if (t == QMetaType::UnknownType) { qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property '%s::%s'", typeName, mobj->className(), name()); return QVariant(); } @@ -2931,7 +2931,7 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const return false; } else if (v.type() != QVariant::Int && v.type() != QVariant::UInt) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) + if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData()) return false; v = QVariant(*reinterpret_cast(v.constData())); } @@ -2952,7 +2952,7 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const t = flags >> 24; typeName = legacyString(mobj, mobj->d.data[handle + 1]); } - if (t == QVariant::Invalid) { + if (t == QMetaType::UnknownType) { const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); const char *vtypeName = value.typeName(); if (vtypeName && strcmp(typeName, vtypeName) == 0) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 003ad1c32d..6d5973a7d0 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -242,6 +242,7 @@ template<> struct TypeDefinition { static const bool IsAvailable = fals \value QEasingCurve QEasingCurve \value User Base value for user types + \value UnknownType This is an invalid type id. It is returned from QMetaType for types that are not registered \omitvalue FirstGuiType \omitvalue FirstWidgetsType @@ -311,7 +312,7 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ QT_FOR_EACH_STATIC_TYPE(QT_ADD_STATIC_METATYPE) QT_FOR_EACH_STATIC_ALIAS_TYPE(QT_ADD_STATIC_METATYPE_ALIASES_ITER) QT_FOR_EACH_STATIC_HACKS_TYPE(QT_ADD_STATIC_METATYPE_HACKS_ITER) - {0, 0, QMetaType::Void} + {0, 0, QMetaType::UnknownType} }; Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = 0; @@ -348,10 +349,7 @@ Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveOp, LoadOperator loadOp) { - int idx = type(typeName); - if (!idx) - return; - registerStreamOperators(idx, saveOp, loadOp); + registerStreamOperators(type(typeName), saveOp, loadOp); } /*! \internal @@ -434,7 +432,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) { const QVector * const ct = customTypes(); if (!ct) - return 0; + return QMetaType::UnknownType; for (int v = 0; v < ct->count(); ++v) { const QCustomTypeInfo &customInfo = ct->at(v); @@ -445,7 +443,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) return v + QMetaType::User; } } - return 0; + return QMetaType::UnknownType; } /*! \internal @@ -488,11 +486,11 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, int previousSize = 0; int previousFlags = 0; - if (!idx) { + if (idx == UnknownType) { QWriteLocker locker(customTypesLock()); idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; inf.creator = creator; @@ -558,12 +556,12 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId) int idx = qMetaTypeStaticType(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QWriteLocker locker(customTypesLock()); idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; inf.alias = aliasId; @@ -592,17 +590,20 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId) */ bool QMetaType::isRegistered(int type) { - if (type >= 0 && type < User) { - // predefined type + // predefined type + if ((type >= FirstCoreType && type <= LastCoreType) + || (type >= FirstGuiType && type <= LastGuiType) + || (type >= FirstWidgetsType && type <= LastWidgetsType)) { return true; } + QReadLocker locker(customTypesLock()); const QVector * const ct = customTypes(); return ((type >= User) && (ct && ct->count() > type - User) && !ct->at(type - User).typeName.isEmpty()); } /*! - Returns a handle to the type called \a typeName, or 0 if there is + Returns a handle to the type called \a typeName, or QMetaType::UnknownType if there is no such type. \sa isRegistered(), typeName(), Type @@ -611,17 +612,17 @@ int QMetaType::type(const char *typeName) { int length = qstrlen(typeName); if (!length) - return 0; + return UnknownType; int type = qMetaTypeStaticType(typeName, length); - if (!type) { + if (type == UnknownType) { QReadLocker locker(customTypesLock()); type = qMetaTypeCustomType_unlocked(typeName, length); #ifndef QT_NO_QOBJECT - if (!type) { + if (type == UnknownType) { const NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); type = qMetaTypeStaticType(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!type) { + if (type == UnknownType) { type = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); } @@ -652,6 +653,7 @@ bool QMetaType::save(QDataStream &stream, int type, const void *data) return false; switch(type) { + case QMetaType::UnknownType: case QMetaType::Void: case QMetaType::VoidStar: case QMetaType::QObjectStar: @@ -857,6 +859,7 @@ bool QMetaType::load(QDataStream &stream, int type, void *data) return false; switch(type) { + case QMetaType::UnknownType: case QMetaType::Void: case QMetaType::VoidStar: case QMetaType::QObjectStar: @@ -1154,6 +1157,7 @@ void *QMetaType::create(int type, const void *copy) case QMetaType::QModelIndex: return new NS(QModelIndex)(*static_cast(copy)); #endif + case QMetaType::UnknownType: case QMetaType::Void: return 0; default: @@ -1257,6 +1261,7 @@ void *QMetaType::create(int type, const void *copy) case QMetaType::QModelIndex: return new NS(QModelIndex); #endif + case QMetaType::UnknownType: case QMetaType::Void: return 0; default: @@ -1318,6 +1323,7 @@ public: template void delegate(const T *where) { DestroyerImpl::Destroy(m_type, const_cast(where)); } void delegate(const void *) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) { customTypeDestroyer(m_type, (void*)where); } private: @@ -1380,6 +1386,7 @@ public: template void *delegate(const T *copy) { return ConstructorImpl::Construct(m_type, m_where, copy); } void *delegate(const void *) { return m_where; } + void *delegate(const QMetaTypeSwitcher::UnknownType*) { return m_where; } void *delegate(const QMetaTypeSwitcher::NotBuiltinType *copy) { return customTypeConstructor(m_type, m_where, copy); } private: @@ -1468,6 +1475,7 @@ public: template void delegate(const T *where) { DestructorImpl::Destruct(m_type, const_cast(where)); } void delegate(const void *) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) { customTypeDestructor(m_type, (void*)where); } private: @@ -1536,6 +1544,7 @@ public: template int delegate(const T*) { return SizeOfImpl::Size(m_type); } + int delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } int delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeSizeOf(m_type); } private: static int customTypeSizeOf(const int type) @@ -1606,13 +1615,14 @@ public: template quint32 delegate(const T*) { return FlagsImpl::Flags(m_type); } quint32 delegate(const void*) { return 0; } + quint32 delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } quint32 delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeFlags(m_type); } private: const int m_type; static quint32 customTypeFlags(const int type) { const QVector * const ct = customTypes(); - if (Q_UNLIKELY(!ct)) + if (Q_UNLIKELY(!ct || type < QMetaType::User)) return 0; QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(ct->count() <= type - QMetaType::User)) @@ -1793,6 +1803,7 @@ public: template void delegate(const T*) { TypeInfoImpl(m_type, info); } void delegate(const void*) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType*) { customTypeInfo(m_type); } private: void customTypeInfo(const uint type) @@ -1813,7 +1824,7 @@ QMetaType QMetaType::typeInfo(const int type) { TypeInfo typeInfo(type); QMetaTypeSwitcher::switcher(typeInfo, type, 0); - return typeInfo.info.creator || !type ? QMetaType(QMetaType::NoExtensionFlags + return typeInfo.info.creator || type == Void ? QMetaType(QMetaType::NoExtensionFlags , static_cast(0) // typeInfo::info is a temporary variable, we can't return address of it. , typeInfo.info.creator , typeInfo.info.deleter @@ -1824,26 +1835,23 @@ QMetaType QMetaType::typeInfo(const int type) , typeInfo.info.size , typeInfo.info.flags , type) - : QMetaType(-1); + : QMetaType(UnknownType); } QMetaType::QMetaType(const int typeId) : m_typeId(typeId) { - if (Q_UNLIKELY(typeId == -1)) { + if (Q_UNLIKELY(typeId == UnknownType)) { // Constructs invalid QMetaType instance. m_extensionFlags = 0xffffffff; Q_ASSERT(!isValid()); } else { // TODO it can be better. *this = QMetaType::typeInfo(typeId); - if (m_typeId > 0 && !m_creator) { + if (m_typeId == UnknownType) m_extensionFlags = 0xffffffff; - m_typeId = -1; - } - if (m_typeId == QMetaType::Void) { + else if (m_typeId == QMetaType::Void) m_extensionFlags = CreateEx | DestroyEx | ConstructEx | DestructEx; - } } } diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index beb7294abd..0f069dc45c 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE // F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType) #define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\ - F(Void, 0, void) \ + F(Void, 43, void) \ F(Bool, 1, bool) \ F(Int, 2, int) \ F(UInt, 3, uint) \ @@ -192,8 +192,8 @@ public: // these are merged with QVariant QT_FOR_EACH_STATIC_TYPE(QT_DEFINE_METATYPE_ID) - FirstCoreType = Void, - LastCoreType = QModelIndex, + FirstCoreType = Bool, + LastCoreType = Void, FirstGuiType = QFont, LastGuiType = QPolygonF, FirstWidgetsType = QIcon, @@ -202,6 +202,7 @@ public: QReal = sizeof(qreal) == sizeof(double) ? Double : Float, + UnknownType = 0, User = 256 }; @@ -627,7 +628,7 @@ inline QMetaType::~QMetaType() inline bool QMetaType::isValid() const { - return m_typeId >= 0; + return m_typeId != UnknownType; } inline bool QMetaType::isRegistered() const diff --git a/src/corelib/kernel/qmetatypeswitcher_p.h b/src/corelib/kernel/qmetatypeswitcher_p.h index e9c15ea214..ffd188c972 100644 --- a/src/corelib/kernel/qmetatypeswitcher_p.h +++ b/src/corelib/kernel/qmetatypeswitcher_p.h @@ -59,7 +59,8 @@ QT_BEGIN_NAMESPACE class QMetaTypeSwitcher { public: - class NotBuiltinType; + class NotBuiltinType; // type is not a built-in type, but it may be a custom type or an unknown type + class UnknownType; // type not known to QMetaType system template static ReturnType switcher(DelegateObject &logic, int type, const void *data); }; @@ -74,7 +75,11 @@ ReturnType QMetaTypeSwitcher::switcher(DelegateObject &logic, int type, const vo switch (QMetaType::Type(type)) { QT_FOR_EACH_STATIC_TYPE(QT_METATYPE_SWICHER_CASE) + case QMetaType::UnknownType: + return logic.delegate(static_cast(data)); default: + if (type < QMetaType::User) + return logic.delegate(static_cast(data)); return logic.delegate(static_cast(data)); } } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 1c18883fde..ae51764c0a 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1697,7 +1697,7 @@ void QVariant::load(QDataStream &s) QByteArray name; s >> name; typeId = QMetaType::type(name); - if (!typeId) { + if (typeId == QMetaType::UnknownType) { s.setStatus(QDataStream::ReadCorruptData); return; } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 5da482d5cd..4d4b2d51ab 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -126,7 +126,7 @@ class Q_CORE_EXPORT QVariant { public: enum Type { - Invalid = QMetaType::Void, + Invalid = QMetaType::UnknownType, Bool = QMetaType::Bool, Int = QMetaType::Int, UInt = QMetaType::UInt, diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index a754bc4363..b28aaf3c20 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -187,7 +187,11 @@ public: return FilteredComparator::compare(m_a, m_b); } - bool delegate(const void*) { return true; } + bool delegate(const void*) { Q_ASSERT(false); return true; } + bool delegate(const QMetaTypeSwitcher::UnknownType*) + { + return true; // for historical reason invalid variant == invalid variant + } bool delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return false; } protected: const QVariant::Private *m_a; @@ -281,7 +285,8 @@ public: return CallIsNull::isNull(m_d); } // we need that as sizof(void) is undefined and it is needed in HasIsNullMethod - bool delegate(const void *) { return m_d->is_null; } + bool delegate(const void *) { Q_ASSERT(false); return m_d->is_null; } + bool delegate(const QMetaTypeSwitcher::UnknownType *) { return m_d->is_null; } bool delegate(const QMetaTypeSwitcher::NotBuiltinType *) { return m_d->is_null; } protected: const QVariant::Private *m_d; @@ -354,8 +359,18 @@ public: void delegate(const void*) { - // QMetaType::Void == QVariant::Invalid, creating an invalid value creates invalid QVariant - // TODO it might go away, check is needed + qWarning("Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead"); + m_x->type = QMetaType::UnknownType; + m_x->is_shared = false; + m_x->is_null = !m_copy; + } + + void delegate(const QMetaTypeSwitcher::UnknownType*) + { + if (m_x->type != QMetaType::UnknownType) { + qWarning("Trying to construct an instance of an invalid type, type id: %i", m_x->type); + m_x->type = QMetaType::UnknownType; + } m_x->is_shared = false; m_x->is_null = !m_copy; } @@ -401,7 +416,8 @@ public: qWarning("Trying to destruct an instance of an invalid type, type id: %i", m_d->type); } // Ignore nonconstructible type - void delegate(const void*) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} + void delegate(const void*) { Q_ASSERT(false); } private: QVariant::Private *m_d; }; @@ -446,10 +462,11 @@ public: { qWarning("Trying to stream an instance of an invalid type, type id: %i", m_d->type); } - void delegate(const void*) + void delegate(const QMetaTypeSwitcher::UnknownType*) { m_debugStream.nospace() << "QVariant::Invalid"; } + void delegate(const void*) { Q_ASSERT(false); } private: QDebug m_debugStream; QVariant::Private *m_d; diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index f86365025f..8d46ee4801 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -686,7 +686,7 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, ++i; // make sure that the output parameters have signatures too - if (returnType != 0 && QDBusMetaType::typeToSignature(returnType) == 0) + if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == 0) continue; bool ok = true; @@ -919,7 +919,7 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q // output arguments QVariantList outputArgs; void *null = 0; - if (metaTypes[0] != QMetaType::Void) { + if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) { QVariant arg(metaTypes[0], null); outputArgs.append( arg ); params[0] = const_cast(outputArgs.at( outputArgs.count() - 1 ).constData()); diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index 7b8a67f343..ce8146f639 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -71,7 +71,6 @@ public: private: struct Method { QList parameterNames; - QByteArray typeName; QByteArray tag; QByteArray name; QVarLengthArray inputTypes; @@ -266,10 +265,7 @@ void QDBusMetaObjectGenerator::parseMethods() mm.outputTypes.append(type.id); - if (i == 0) { - // return value - mm.typeName = type.name; - } else { + if (i != 0) { // non-const ref parameter mm.parameterNames.append(arg.name.toLatin1()); @@ -477,7 +473,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) typeName = QMetaType::typeName(type); typeName.append('&'); } - Q_ASSERT(type || (i < 0)); + Q_ASSERT(type != QMetaType::UnknownType); int typeInfo; if (!typeName.isEmpty()) typeInfo = IsUnresolvedType | strings.enter(typeName); @@ -516,7 +512,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) // form is name, typeinfo, flags idata[offset++] = strings.enter(it.key()); // name - Q_ASSERT(mp.type != 0); + Q_ASSERT(mp.type != QMetaType::UnknownType); idata[offset++] = mp.type; idata[offset++] = mp.flags; diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp index b0d640608a..7c8d8cf679 100644 --- a/src/dbus/qdbusmetatype.cpp +++ b/src/dbus/qdbusmetatype.cpp @@ -311,7 +311,7 @@ bool QDBusMetaType::demarshall(const QDBusArgument &arg, int id, void *data) int QDBusMetaType::signatureToType(const char *signature) { if (!signature) - return QVariant::Invalid; + return QMetaType::UnknownType; QDBusMetaTypeId::init(); switch (signature[0]) @@ -378,7 +378,7 @@ int QDBusMetaType::signatureToType(const char *signature) } // fall through default: - return QVariant::Invalid; + return QMetaType::UnknownType; } } diff --git a/src/dbus/qdbusmisc.cpp b/src/dbus/qdbusmisc.cpp index 0a5da604f2..88bab88a13 100644 --- a/src/dbus/qdbusmisc.cpp +++ b/src/dbus/qdbusmisc.cpp @@ -170,7 +170,7 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList& metaTypes) } int id = QMetaType::type(type); - if (id == 0) { + if (id == QMetaType::UnknownType) { //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // invalid type in method parameter list return -1; diff --git a/src/testlib/qsignaldumper.cpp b/src/testlib/qsignaldumper.cpp index e15add0c8d..c7b9f31b84 100644 --- a/src/testlib/qsignaldumper.cpp +++ b/src/testlib/qsignaldumper.cpp @@ -112,7 +112,8 @@ static void qSignalDumperCallback(QObject *caller, int method_index, void **argv quintptr addr = quintptr(*reinterpret_cast(argv[i + 1])); str.append(QByteArray::number(addr, 16)); - } else if (typeId != QMetaType::Void) { + } else if (typeId != QMetaType::UnknownType) { + Q_ASSERT(typeId != QMetaType::Void); // void parameter => metaobject is corrupt str.append(arg) .append('(') .append(QVariant(typeId, argv[i + 1]).toString().toLocal8Bit()) diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h index eb52ebf706..70944baadf 100644 --- a/src/testlib/qsignalspy.h +++ b/src/testlib/qsignalspy.h @@ -122,9 +122,11 @@ private: QList params = member.parameterTypes(); for (int i = 0; i < params.count(); ++i) { int tp = QMetaType::type(params.at(i).constData()); - if (tp == QMetaType::Void) + if (tp == QMetaType::UnknownType) { + Q_ASSERT(tp != QMetaType::Void); // void parameter => metaobject is corrupt qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.", params.at(i).constData()); + } args << tp; } } diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 73a0605028..1284518fc5 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -60,7 +60,7 @@ uint nameToBuiltinType(const QByteArray &name) return 0; uint tp = QMetaType::type(name.constData()); - return tp < QMetaType::User ? tp : 0; + return tp < uint(QMetaType::User) ? tp : QMetaType::UnknownType; } /* @@ -69,7 +69,7 @@ uint nameToBuiltinType(const QByteArray &name) bool isBuiltinType(const QByteArray &type) { int id = QMetaType::type(type.constData()); - if (!id && !type.isEmpty() && type != "void") + if (id == QMetaType::UnknownType) return false; return (id < QMetaType::User); } @@ -632,7 +632,7 @@ void Generator::generateFunctionParameters(const QList& list, const else fprintf(out, "%4d", type); } else { - Q_ASSERT(!typeName.isEmpty()); + Q_ASSERT(!typeName.isEmpty() || f.isConstructor); fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(typeName)); } fputc(',', out); @@ -1097,8 +1097,9 @@ void Generator::generateStaticMetacall() fprintf(out, " switch (_id) {\n"); for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) { const FunctionDef &f = methodList.at(methodindex); + Q_ASSERT(!f.normalizedType.isEmpty()); fprintf(out, " case %d: ", methodindex); - if (f.normalizedType.size()) + if (f.normalizedType != "void") fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); fprintf(out, "_t->"); if (f.inPrivateClass.size()) @@ -1113,7 +1114,7 @@ void Generator::generateStaticMetacall() isUsed_a = true; } fprintf(out, ");"); - if (f.normalizedType.size()) { + if (f.normalizedType != "void") { fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ", noRef(f.normalizedType).constData()); isUsed_a = true; @@ -1192,7 +1193,8 @@ void Generator::generateSignal(FunctionDef *def,int index) constQualifier = "const"; } - if (def->arguments.isEmpty() && def->normalizedType.isEmpty()) { + Q_ASSERT(!def->normalizedType.isEmpty()); + if (def->arguments.isEmpty() && def->normalizedType == "void") { fprintf(out, ")%s\n{\n" " QMetaObject::activate(%s, &staticMetaObject, %d, 0);\n" "}\n", constQualifier, thisPtr.constData(), index); @@ -1207,11 +1209,11 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, "%s _t%d%s", a.type.name.constData(), offset++, a.rightType.constData()); } fprintf(out, ")%s\n{\n", constQualifier); - if (def->type.name.size() && def->normalizedType.size()) + if (def->type.name.size() && def->normalizedType != "void") fprintf(out, " %s _t0 = %s();\n", noRef(def->normalizedType).constData(), noRef(def->normalizedType).constData()); fprintf(out, " void *_a[] = { "); - if (def->normalizedType.isEmpty()) { + if (def->normalizedType == "void") { fprintf(out, "0"); } else { if (def->returnTypeIsVolatile) @@ -1227,7 +1229,7 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, ", const_cast(reinterpret_cast(&_t%d))", i); fprintf(out, " };\n"); fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); - if (def->normalizedType.size()) + if (def->normalizedType != "void") fprintf(out, " return _t0;\n"); fprintf(out, "}\n"); } diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 49fc29592d..467f85c599 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -75,9 +75,7 @@ static QByteArray normalizeType(const QByteArray &ba, bool fixScope = false) } } *d = '\0'; - QByteArray result; - if (strncmp("void", buf, d - buf) != 0) - result = normalizeTypeInternal(buf, d, fixScope); + QByteArray result = normalizeTypeInternal(buf, d, fixScope); if (buf != stackbuf) delete [] buf; return result; diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index 60c8fdb2f2..f44a671180 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -223,7 +223,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject()") << QByteArray("MethodTestObject()") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList()) << (QList()) << (QList()) @@ -259,7 +259,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(int)") << QByteArray("MethodTestObject(int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList() << int(QMetaType::Int)) << (QList() << QByteArray("int")) << (QList() << QByteArray("constructorIntArg")) @@ -295,7 +295,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(qreal)") << QByteArray("MethodTestObject(qreal)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList() << qMetaTypeId()) << (QList() << QByteArray("qreal")) << (QList() << QByteArray("constructorQRealArg")) @@ -331,7 +331,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(QString)") << QByteArray("MethodTestObject(QString)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList() << int(QMetaType::QString)) << (QList() << QByteArray("QString")) << (QList() << QByteArray("constructorQStringArg")) @@ -367,7 +367,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(CustomType)") << QByteArray("MethodTestObject(CustomType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList() << qMetaTypeId()) << (QList() << QByteArray("CustomType")) << (QList() << QByteArray("constructorCustomTypeArg")) @@ -403,7 +403,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(CustomUnregisteredType)") << QByteArray("MethodTestObject(CustomUnregisteredType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList() << 0) << (QList() << QByteArray("CustomUnregisteredType")) << (QList() << QByteArray("constructorCustomUnregisteredTypeArg")) @@ -536,7 +536,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") << QByteArray("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << parameterTypes << parameterTypeNames << parameterNames << QMetaMethod::Public << QMetaMethod::Constructor; @@ -571,7 +571,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(bool,int)") << QByteArray("MethodTestObject(bool,int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList() << QByteArray("bool") << QByteArray("int")) << (QList() << QByteArray("") << QByteArray("")) @@ -616,7 +616,6 @@ void tst_QMetaMethod::method() QCOMPARE(method.name(), computedName); QCOMPARE(method.tag(), ""); - QCOMPARE(method.returnType(), returnType); if (QByteArray(method.typeName()) != returnTypeName) { // QMetaMethod should always produce a semantically equivalent typename diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 72913d10f2..4f283cef90 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -315,6 +315,7 @@ void tst_QMetaType::typeName_data() QT_FOR_EACH_STATIC_TYPE(TYPENAME_DATA) QT_FOR_EACH_STATIC_ALIAS_TYPE(TYPENAME_DATA_ALIAS) + QTest::newRow("QMetaType::UnknownType") << QMetaType::UnknownType << static_cast(0); } void tst_QMetaType::typeName() @@ -625,6 +626,8 @@ void tst_QMetaType::sizeOf_data() { QTest::addColumn("type"); QTest::addColumn("size"); + + QTest::newRow("QMetaType::UnknownType") << QMetaType::UnknownType << 0; #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << QMetaType::MetaTypeName << int(QTypeInfo::sizeOf); FOR_EACH_CORE_METATYPE(ADD_METATYPE_TEST_ROW) @@ -984,6 +987,7 @@ void tst_QMetaType::isRegistered_data() QTest::newRow("-1") << -1 << false; QTest::newRow("-42") << -42 << false; QTest::newRow("IsRegisteredDummyType + 1") << (dummyTypeId + 1) << false; + QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << false; } void tst_QMetaType::isRegistered() diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index ccdab17668..ffe1d2bec4 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -3542,6 +3542,10 @@ void tst_QVariant::loadQVariantFromDataStream(QDataStream::Version version) stream >> typeName >> loadedVariant; const int id = QMetaType::type(typeName.toLatin1()); + if (id == QMetaType::Void) { + // Void type is not supported by QVariant + return; + } QVariant constructedVariant(static_cast(id)); QCOMPARE(constructedVariant.userType(), id); @@ -3561,6 +3565,10 @@ void tst_QVariant::saveQVariantFromDataStream(QDataStream::Version version) dataFileStream >> typeName; QByteArray data = file.readAll(); const int id = QMetaType::type(typeName.toLatin1()); + if (id == QMetaType::Void) { + // Void type is not supported by QVariant + return; + } QBuffer buffer; buffer.open(QIODevice::ReadWrite); @@ -3621,7 +3629,9 @@ void tst_QVariant::debugStream_data() const char *tagName = QMetaType::typeName(id); if (!tagName) continue; - QTest::newRow(tagName) << QVariant(static_cast(id)) << id; + if (id != QMetaType::Void) { + QTest::newRow(tagName) << QVariant(static_cast(id)) << id; + } } QTest::newRow("QBitArray(111)") << QVariant(QBitArray(3, true)) << qMetaTypeId(); QTest::newRow("CustomStreamableClass") << QVariant(qMetaTypeId(), 0) << qMetaTypeId(); From 7e4f32993498db0e06346e32458a1ec7d0c7b3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Tue, 21 Feb 2012 14:51:22 +0100 Subject: [PATCH 73/74] Base QList::setSharable on RefCount::setSharable Change-Id: I2acccdf9ee595a0eee33c9f7ddded9cc121412c1 Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qlist.cpp | 4 +- src/corelib/tools/qlist.h | 35 +++- tests/auto/corelib/tools/qlist/tst_qlist.cpp | 172 +++++++++++++++++++ 3 files changed, 205 insertions(+), 6 deletions(-) diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index dbd026e65a..1b6610a724 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE the number of elements in the list. */ -const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, true, { 0 } }; +const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }; static int grow(int size) { @@ -88,7 +88,6 @@ QListData::Data *QListData::detach_grow(int *idx, int num) Q_CHECK_PTR(t); t->ref.initializeOwned(); - t->sharable = true; t->alloc = alloc; // The space reservation algorithm's optimization is biased towards appending: // Something which looks like an append will put the data at the beginning, @@ -130,7 +129,6 @@ QListData::Data *QListData::detach(int alloc) Q_CHECK_PTR(t); t->ref.initializeOwned(); - t->sharable = true; t->alloc = alloc; if (!alloc) { t->begin = 0; diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 798351cd61..bc3350f42b 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -71,7 +71,6 @@ struct Q_CORE_EXPORT QListData { struct Data { QtPrivate::RefCount ref; int alloc, begin, end; - uint sharable : 1; void *array[1]; }; enum { DataHeaderSize = sizeof(Data) - sizeof(void *) }; @@ -114,7 +113,7 @@ class QList public: inline QList() : d(const_cast(&QListData::shared_null)) { } - inline QList(const QList &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + QList(const QList &l); ~QList(); QList &operator=(const QList &l); #ifdef Q_COMPILER_RVALUE_REFS @@ -142,7 +141,15 @@ public: } inline bool isDetached() const { return !d->ref.isShared(); } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QListData::shared_null) d->sharable = sharable; } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != &QListData::shared_null) + d->ref.setSharable(sharable); + } inline bool isSharedWith(const QList &other) const { return d == other.d; } inline bool isEmpty() const { return p.isEmpty(); } @@ -702,6 +709,28 @@ Q_OUTOFLINE_TEMPLATE void QList::detach_helper() detach_helper(d->alloc); } +template +Q_OUTOFLINE_TEMPLATE QList::QList(const QList &l) + : d(l.d) +{ + if (!d->ref.ref()) { + p.detach(d->alloc); + + struct Cleanup + { + Cleanup(QListData::Data *d) : d_(d) {} + ~Cleanup() { if (d_) qFree(d_); } + + QListData::Data *d_; + } tryCatch(d); + + node_copy(reinterpret_cast(p.begin()), + reinterpret_cast(p.end()), + reinterpret_cast(l.p.begin())); + tryCatch.d_ = 0; + } +} + template Q_OUTOFLINE_TEMPLATE QList::~QList() { diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index fbb821c730..3baa47f013 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -54,6 +54,9 @@ class tst_QList : public QObject Q_OBJECT private slots: + void init(); + void cleanup(); + void length() const; void lengthSignature() const; void append() const; @@ -90,8 +93,100 @@ private slots: void initializeList() const; void const_shared_null() const; + void setSharable1_data() const; + void setSharable1() const; + void setSharable2_data() const; + void setSharable2() const; + +private: + int dummyForGuard; }; +struct Complex +{ + Complex(int val) + : value(val) + , checkSum(this) + { + ++liveCount; + } + + Complex(Complex const &other) + : value(other.value) + , checkSum(this) + { + ++liveCount; + } + + Complex &operator=(Complex const &other) + { + check(); other.check(); + + value = other.value; + return *this; + } + + ~Complex() + { + --liveCount; + check(); + } + + operator int() const { return value; } + + bool operator==(Complex const &other) const + { + check(); other.check(); + return value == other.value; + } + + bool check() const + { + if (this != checkSum) { + ++errorCount; + return false; + } + return true; + } + + struct Guard + { + Guard() : initialLiveCount(liveCount) {} + ~Guard() { if (liveCount != initialLiveCount) ++errorCount; } + + private: + Q_DISABLE_COPY(Guard); + int initialLiveCount; + }; + + static void resetErrors() { errorCount = 0; } + static int errors() { return errorCount; } + +private: + static int errorCount; + static int liveCount; + + int value; + void *checkSum; +}; + +int Complex::errorCount = 0; +int Complex::liveCount = 0; + +void tst_QList::init() +{ + Complex::resetErrors(); + new (&dummyForGuard) Complex::Guard(); +} + +void tst_QList::cleanup() +{ + QCOMPARE(Complex::errors(), 0); + + reinterpret_cast(&dummyForGuard)->~Guard(); + QCOMPARE(Complex::errors(), 0); +} + void tst_QList::length() const { /* Empty list. */ @@ -696,5 +791,82 @@ void tst_QList::const_shared_null() const QVERIFY(!list2.isDetached()); } +Q_DECLARE_METATYPE(QList); +Q_DECLARE_METATYPE(QList); + +template +void generateSetSharableData() +{ + QTest::addColumn >("list"); + QTest::addColumn("size"); + + QTest::newRow("null") << QList() << 0; + QTest::newRow("non-empty") << (QList() << T(0) << T(1) << T(2) << T(3) << T(4)) << 5; +} + +template +void runSetSharableTest() +{ + QFETCH(QList, list); + QFETCH(int, size); + + QVERIFY(!list.isDetached()); // Shared with QTest + + list.setSharable(true); + + QCOMPARE(list.size(), size); + + { + QList copy(list); + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + list.setSharable(false); + QVERIFY(list.isDetached() || list.isSharedWith(QList())); + + { + QList copy(list); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QList())); + QCOMPARE(copy.size(), size); + QCOMPARE(copy, list); + } + + list.setSharable(true); + + { + QList copy(list); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + for (int i = 0; i < list.size(); ++i) + QCOMPARE(int(list[i]), i); + + QCOMPARE(list.size(), size); +} + +void tst_QList::setSharable1_data() const +{ + generateSetSharableData(); +} + +void tst_QList::setSharable2_data() const +{ + generateSetSharableData(); +} + +void tst_QList::setSharable1() const +{ + runSetSharableTest(); +} + +void tst_QList::setSharable2() const +{ + runSetSharableTest(); +} + QTEST_APPLESS_MAIN(tst_QList) #include "tst_qlist.moc" From 8141e34280a92088a527e0935765ad8ba8e92be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Abecasis?= Date: Thu, 8 Mar 2012 11:48:26 +0100 Subject: [PATCH 74/74] Skip test when implicit move operators not available Besides rvalue-references, this test depends on the compiler to generate implicit move operators on a derived class, based on the ones available on its base class. At least Visual Studio 2010 and some variations of clang 3.0 are known not to generate implicit move constructors and assignment operators. Gcc 4.6 and up seem to support the feature. Change-Id: Ied464ef678f517321b19f8a7bacddb6cd6665585 Reviewed-by: Kent Hansen --- .../tools/qarraydata/tst_qarraydata.cpp | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index f3f1daba0f..884f4f7d1d 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1356,11 +1356,54 @@ typename RemoveReference::Type &&cxx11Move(T &&t) { return static_cast::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 + { + }; +}; #endif void tst_QArrayData::rValueReferences() { #ifdef Q_COMPILER_RVALUE_REFS + if (!CompilerHasCxx11ImplicitMoves::value()) + QSKIP("Implicit move ctor not supported in current configuration"); + SimpleVector v1(1, 42); SimpleVector v2;