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:
João Abecasis 2011-11-03 12:52:26 +01:00 committed by Qt by Nokia
parent bd0b49efe0
commit 4da0b5fc02
4 changed files with 323 additions and 7 deletions

View 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

View File

@ -3,6 +3,7 @@
HEADERS += \
tools/qalgorithms.h \
tools/qarraydata.h \
tools/qarraydataops.h \
tools/qbitarray.h \
tools/qbytearray.h \
tools/qbytearraymatcher.h \

View File

@ -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);
}
}

View File

@ -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"