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: 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 + tests/auto/corelib/tools/qarraydata/simplevector.h | 20 ++- .../corelib/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" -- cgit v1.2.3