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 <bradley.hughes@nokia.com>
This commit is contained in:
parent
bd0b49efe0
commit
4da0b5fc02
182
src/corelib/tools/qarraydataops.h
Normal file
182
src/corelib/tools/qarraydataops.h
Normal file
@ -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 <QtCore/qarraydata.h>
|
||||
|
||||
#include <new>
|
||||
#include <string.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Core)
|
||||
|
||||
namespace QtPrivate {
|
||||
|
||||
template <class T>
|
||||
struct QPodArrayOps
|
||||
: QTypedArrayData<T>
|
||||
{
|
||||
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 <class T>
|
||||
struct QGenericArrayOps
|
||||
: QTypedArrayData<T>
|
||||
{
|
||||
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 <class T>
|
||||
struct QMovableArrayOps
|
||||
: QGenericArrayOps<T>
|
||||
{
|
||||
// using QGenericArrayOps<T>::copyAppend;
|
||||
// using QGenericArrayOps<T>::destroyAll;
|
||||
};
|
||||
|
||||
template <class T, class = void>
|
||||
struct QArrayOpsSelector
|
||||
{
|
||||
typedef QGenericArrayOps<T> Type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct QArrayOpsSelector<T,
|
||||
typename QEnableIf<
|
||||
!QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic
|
||||
>::Type>
|
||||
{
|
||||
typedef QPodArrayOps<T> Type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct QArrayOpsSelector<T,
|
||||
typename QEnableIf<
|
||||
QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic
|
||||
>::Type>
|
||||
{
|
||||
typedef QMovableArrayOps<T> Type;
|
||||
};
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
template <class T>
|
||||
struct QArrayDataOps
|
||||
: QtPrivate::QArrayOpsSelector<T>::Type
|
||||
{
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // include guard
|
@ -3,6 +3,7 @@
|
||||
HEADERS += \
|
||||
tools/qalgorithms.h \
|
||||
tools/qarraydata.h \
|
||||
tools/qarraydataops.h \
|
||||
tools/qbitarray.h \
|
||||
tools/qbytearray.h \
|
||||
tools/qbytearraymatcher.h \
|
||||
|
@ -44,6 +44,8 @@
|
||||
#define QARRAY_TEST_SIMPLE_VECTOR_H
|
||||
|
||||
#include <QtCore/qarraydata.h>
|
||||
#include <QtCore/qarraydataops.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
template <class T>
|
||||
@ -51,6 +53,7 @@ struct SimpleVector
|
||||
{
|
||||
private:
|
||||
typedef QTypedArrayData<T> Data;
|
||||
typedef QArrayDataOps<T> 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<DataOps *>(d)->copyAppend(n, t);
|
||||
}
|
||||
|
||||
SimpleVector(const T *begin, const T *end)
|
||||
: d(Data::allocate(end - begin))
|
||||
{
|
||||
if (end - begin)
|
||||
static_cast<DataOps *>(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<DataOps *>(d)->destroyAll();
|
||||
Data::deallocate(d);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/qarraydata.h>
|
||||
|
||||
#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<int> v1;
|
||||
SimpleVector<int> v2(v1);
|
||||
SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0));
|
||||
@ -172,6 +176,7 @@ void tst_QArrayData::simpleVector()
|
||||
SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0));
|
||||
SimpleVector<int> v6(static_cast<QTypedArrayData<int> *>(&data1.header));
|
||||
SimpleVector<int> v7(10, 5);
|
||||
SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array));
|
||||
|
||||
v3 = v1;
|
||||
v1.swap(v3);
|
||||
@ -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<int>::isComplex && !QTypeInfo<int>::isStatic);
|
||||
QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic);
|
||||
QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic);
|
||||
|
||||
QCOMPARE(CountedObject::liveCount, size_t(5));
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
QCOMPARE(objArray[i].id, i);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// copyAppend (I)
|
||||
SimpleVector<int> vi(intArray, intArray + 5);
|
||||
SimpleVector<QString> vs(stringArray, stringArray + 5);
|
||||
SimpleVector<CountedObject> vo(objArray, objArray + 5);
|
||||
|
||||
QCOMPARE(CountedObject::liveCount, size_t(10));
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
QCOMPARE(vi[i], intArray[i]);
|
||||
QVERIFY(vs[i].isSharedWith(stringArray[i]));
|
||||
QCOMPARE(vo[i].id, objArray[i].id);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// destroyAll
|
||||
vi.clear();
|
||||
vs.clear();
|
||||
vo.clear();
|
||||
|
||||
QCOMPARE(CountedObject::liveCount, size_t(5));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// copyAppend (II)
|
||||
int referenceInt = 7;
|
||||
QString referenceString = QLatin1String("reference");
|
||||
CountedObject referenceObject;
|
||||
|
||||
vi = SimpleVector<int>(5, referenceInt);
|
||||
vs = SimpleVector<QString>(5, referenceString);
|
||||
vo = SimpleVector<CountedObject>(5, referenceObject);
|
||||
|
||||
QCOMPARE(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"
|
||||
|
Loading…
Reference in New Issue
Block a user