Rewrite QMap to use a RB tree
QMap used to use a skiplist in Qt 4.x, which has variable sized nodes and we can thus not optimise using custom allocators. The rewrite now uses a red-black tree, and all allocations and tree operations happen in the cpp file. This will allow us to introduce custom allocation schemes in later versions of Qt. Added some more tests and a benchmark. Memory consumption of the new QMap implementation is pretty much the same as before. Performance of insertion and lookup has increased by 10-30%. iteration is slower, but still extremely fast and should not matter compared to the work usually done when iterating. Change-Id: I8796c0e4b207d01111e2ead7ae55afb464dd88f5 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
3f7741fbe7
commit
5cb0368516
@ -385,7 +385,6 @@ Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &ma
|
||||
in >> n;
|
||||
|
||||
map.detach();
|
||||
map.setInsertInOrder(true);
|
||||
for (quint32 i = 0; i < n; ++i) {
|
||||
if (in.status() != QDataStream::Ok)
|
||||
break;
|
||||
@ -395,7 +394,6 @@ Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &ma
|
||||
in >> key >> value;
|
||||
map.insertMulti(key, value);
|
||||
}
|
||||
map.setInsertInOrder(false);
|
||||
if (in.status() != QDataStream::Ok)
|
||||
map.clear();
|
||||
if (oldStatus != QDataStream::Ok)
|
||||
|
@ -50,171 +50,317 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
const QMapData QMapData::shared_null = {
|
||||
const_cast<QMapData *>(&shared_null),
|
||||
{ const_cast<QMapData *>(&shared_null), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, false, true, false, 0
|
||||
};
|
||||
const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, 0, 0 } };
|
||||
|
||||
QMapData *QMapData::createData(int alignment)
|
||||
const QMapNodeBase *QMapNodeBase::nextNode() const
|
||||
{
|
||||
QMapData *d = new QMapData;
|
||||
Q_CHECK_PTR(d);
|
||||
Node *e = reinterpret_cast<Node *>(d);
|
||||
e->backward = e;
|
||||
e->forward[0] = e;
|
||||
const QMapNodeBase *n = this;
|
||||
if (n->right) {
|
||||
n = n->right;
|
||||
while (n->left)
|
||||
n = n->left;
|
||||
} else {
|
||||
const QMapNodeBase *y = n->parent();
|
||||
while (y && n == y->right) {
|
||||
n = y;
|
||||
y = n->parent();
|
||||
}
|
||||
n = y;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
const QMapNodeBase *QMapNodeBase::previousNode() const
|
||||
{
|
||||
const QMapNodeBase *n = this;
|
||||
if (n->left) {
|
||||
n = n->left;
|
||||
while (n->right)
|
||||
n = n->right;
|
||||
} else {
|
||||
const QMapNodeBase *y = n->parent();
|
||||
while (y && n == y->left) {
|
||||
n = y;
|
||||
y = n->parent();
|
||||
}
|
||||
n = y;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void QMapDataBase::rotateLeft(QMapNodeBase *x)
|
||||
{
|
||||
QMapNodeBase *&root = header.left;
|
||||
QMapNodeBase *y = x->right;
|
||||
x->right = y->left;
|
||||
if (y->left != 0)
|
||||
y->left->setParent(x);
|
||||
y->setParent(x->parent());
|
||||
if (x == root)
|
||||
root = y;
|
||||
else if (x == x->parent()->left)
|
||||
x->parent()->left = y;
|
||||
else
|
||||
x->parent()->right = y;
|
||||
y->left = x;
|
||||
x->setParent(y);
|
||||
}
|
||||
|
||||
|
||||
void QMapDataBase::rotateRight(QMapNodeBase *x)
|
||||
{
|
||||
QMapNodeBase *&root = header.left;
|
||||
QMapNodeBase *y = x->left;
|
||||
x->left = y->right;
|
||||
if (y->right != 0)
|
||||
y->right->setParent(x);
|
||||
y->setParent(x->parent());
|
||||
if (x == root)
|
||||
root = y;
|
||||
else if (x == x->parent()->right)
|
||||
x->parent()->right = y;
|
||||
else
|
||||
x->parent()->left = y;
|
||||
y->right = x;
|
||||
x->setParent(y);
|
||||
}
|
||||
|
||||
|
||||
void QMapDataBase::rebalance(QMapNodeBase *x)
|
||||
{
|
||||
QMapNodeBase *&root = header.left;
|
||||
x->setColor(QMapNodeBase::Red);
|
||||
while (x != root && x->parent()->color() == QMapNodeBase::Red) {
|
||||
if (x->parent() == x->parent()->parent()->left) {
|
||||
QMapNodeBase *y = x->parent()->parent()->right;
|
||||
if (y && y->color() == QMapNodeBase::Red) {
|
||||
x->parent()->setColor(QMapNodeBase::Black);
|
||||
y->setColor(QMapNodeBase::Black);
|
||||
x->parent()->parent()->setColor(QMapNodeBase::Red);
|
||||
x = x->parent()->parent();
|
||||
} else {
|
||||
if (x == x->parent()->right) {
|
||||
x = x->parent();
|
||||
rotateLeft(x);
|
||||
}
|
||||
x->parent()->setColor(QMapNodeBase::Black);
|
||||
x->parent()->parent()->setColor(QMapNodeBase::Red);
|
||||
rotateRight (x->parent()->parent());
|
||||
}
|
||||
} else {
|
||||
QMapNodeBase *y = x->parent()->parent()->left;
|
||||
if (y && y->color() == QMapNodeBase::Red) {
|
||||
x->parent()->setColor(QMapNodeBase::Black);
|
||||
y->setColor(QMapNodeBase::Black);
|
||||
x->parent()->parent()->setColor(QMapNodeBase::Red);
|
||||
x = x->parent()->parent();
|
||||
} else {
|
||||
if (x == x->parent()->left) {
|
||||
x = x->parent();
|
||||
rotateRight(x);
|
||||
}
|
||||
x->parent()->setColor(QMapNodeBase::Black);
|
||||
x->parent()->parent()->setColor(QMapNodeBase::Red);
|
||||
rotateLeft(x->parent()->parent());
|
||||
}
|
||||
}
|
||||
}
|
||||
root->setColor(QMapNodeBase::Black);
|
||||
}
|
||||
|
||||
void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
|
||||
{
|
||||
QMapNodeBase *&root = header.left;
|
||||
QMapNodeBase *y = z;
|
||||
QMapNodeBase *x;
|
||||
QMapNodeBase *x_parent;
|
||||
if (y->left == 0) {
|
||||
x = y->right;
|
||||
} else {
|
||||
if (y->right == 0) {
|
||||
x = y->left;
|
||||
} else {
|
||||
y = y->right;
|
||||
while (y->left != 0)
|
||||
y = y->left;
|
||||
x = y->right;
|
||||
}
|
||||
}
|
||||
if (y != z) {
|
||||
z->left->setParent(y);
|
||||
y->left = z->left;
|
||||
if (y != z->right) {
|
||||
x_parent = y->parent();
|
||||
if (x)
|
||||
x->setParent(y->parent());
|
||||
y->parent()->left = x;
|
||||
y->right = z->right;
|
||||
z->right->setParent(y);
|
||||
} else {
|
||||
x_parent = y;
|
||||
}
|
||||
if (root == z)
|
||||
root = y;
|
||||
else if (z->parent()->left == z)
|
||||
z->parent()->left = y;
|
||||
else
|
||||
z->parent()->right = y;
|
||||
y->setParent(z->parent());
|
||||
// Swap the colors
|
||||
QMapNodeBase::Color c = y->color();
|
||||
y->setColor(z->color());
|
||||
z->setColor(c);
|
||||
y = z;
|
||||
} else {
|
||||
x_parent = y->parent();
|
||||
if (x)
|
||||
x->setParent(y->parent());
|
||||
if (root == z)
|
||||
root = x;
|
||||
else if (z->parent()->left == z)
|
||||
z->parent()->left = x;
|
||||
else
|
||||
z->parent()->right = x;
|
||||
}
|
||||
if (y->color() != QMapNodeBase::Red) {
|
||||
while (x != root && (x == 0 || x->color() == QMapNodeBase::Black)) {
|
||||
if (x == x_parent->left) {
|
||||
QMapNodeBase *w = x_parent->right;
|
||||
if (w->color() == QMapNodeBase::Red) {
|
||||
w->setColor(QMapNodeBase::Black);
|
||||
x_parent->setColor(QMapNodeBase::Red);
|
||||
rotateLeft(x_parent);
|
||||
w = x_parent->right;
|
||||
}
|
||||
if ((w->left == 0 || w->left->color() == QMapNodeBase::Black) &&
|
||||
(w->right == 0 || w->right->color() == QMapNodeBase::Black)) {
|
||||
w->setColor(QMapNodeBase::Red);
|
||||
x = x_parent;
|
||||
x_parent = x_parent->parent();
|
||||
} else {
|
||||
if (w->right == 0 || w->right->color() == QMapNodeBase::Black) {
|
||||
if (w->left)
|
||||
w->left->setColor(QMapNodeBase::Black);
|
||||
w->setColor(QMapNodeBase::Red);
|
||||
rotateRight(w);
|
||||
w = x_parent->right;
|
||||
}
|
||||
w->setColor(x_parent->color());
|
||||
x_parent->setColor(QMapNodeBase::Black);
|
||||
if (w->right)
|
||||
w->right->setColor(QMapNodeBase::Black);
|
||||
rotateLeft(x_parent);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
QMapNodeBase *w = x_parent->left;
|
||||
if (w->color() == QMapNodeBase::Red) {
|
||||
w->setColor(QMapNodeBase::Black);
|
||||
x_parent->setColor(QMapNodeBase::Red);
|
||||
rotateRight(x_parent);
|
||||
w = x_parent->left;
|
||||
}
|
||||
if ((w->right == 0 || w->right->color() == QMapNodeBase::Black) &&
|
||||
(w->left == 0 || w->left->color() == QMapNodeBase::Black)) {
|
||||
w->setColor(QMapNodeBase::Red);
|
||||
x = x_parent;
|
||||
x_parent = x_parent->parent();
|
||||
} else {
|
||||
if (w->left == 0 || w->left->color() == QMapNodeBase::Black) {
|
||||
if (w->right)
|
||||
w->right->setColor(QMapNodeBase::Black);
|
||||
w->setColor(QMapNodeBase::Red);
|
||||
rotateLeft(w);
|
||||
w = x_parent->left;
|
||||
}
|
||||
w->setColor(x_parent->color());
|
||||
x_parent->setColor(QMapNodeBase::Black);
|
||||
if (w->left)
|
||||
w->left->setColor(QMapNodeBase::Black);
|
||||
rotateRight(x_parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x)
|
||||
x->setColor(QMapNodeBase::Black);
|
||||
}
|
||||
free(y);
|
||||
--size;
|
||||
}
|
||||
|
||||
static inline int qMapAlignmentThreshold()
|
||||
{
|
||||
// malloc on 32-bit platforms should return pointers that are 8-byte
|
||||
// aligned or more while on 64-bit platforms they should be 16-byte aligned
|
||||
// or more
|
||||
return 2 * sizeof(void*);
|
||||
}
|
||||
|
||||
static inline void *qMapAllocate(int alloc, int alignment)
|
||||
{
|
||||
return alignment > qMapAlignmentThreshold()
|
||||
? qMallocAligned(alloc, alignment)
|
||||
: ::malloc(alloc);
|
||||
}
|
||||
|
||||
static inline void qMapDeallocate(QMapNodeBase *node, int alignment)
|
||||
{
|
||||
if (alignment > qMapAlignmentThreshold())
|
||||
qFreeAligned(node);
|
||||
else
|
||||
::free(node);
|
||||
}
|
||||
|
||||
QMapNodeBase *QMapDataBase::createNode(int alloc, int alignment, QMapNodeBase *parent, bool left)
|
||||
{
|
||||
QMapNodeBase *node = static_cast<QMapNodeBase *>(qMapAllocate(alloc, alignment));
|
||||
Q_CHECK_PTR(node);
|
||||
|
||||
memset(node, 0, alloc);
|
||||
++size;
|
||||
|
||||
if (parent) {
|
||||
if (left) {
|
||||
parent->left = node;
|
||||
} else {
|
||||
parent->right = node;
|
||||
}
|
||||
node->setParent(parent);
|
||||
rebalance(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void QMapDataBase::freeTree(QMapNodeBase *root, int alignment)
|
||||
{
|
||||
if (root->left)
|
||||
freeTree(root->left, alignment);
|
||||
if (root->right)
|
||||
freeTree(root->right, alignment);
|
||||
qMapDeallocate(root, alignment);
|
||||
}
|
||||
|
||||
QMapDataBase *QMapDataBase::createData()
|
||||
{
|
||||
QMapDataBase *d = new QMapDataBase;
|
||||
|
||||
d->ref.initializeOwned();
|
||||
d->topLevel = 0;
|
||||
d->size = 0;
|
||||
d->randomBits = 0;
|
||||
d->insertInOrder = false;
|
||||
d->sharable = true;
|
||||
d->strictAlignment = alignment > 8;
|
||||
d->reserved = 0;
|
||||
|
||||
d->header.p = 0;
|
||||
d->header.left = 0;
|
||||
d->header.right = 0;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void QMapData::continueFreeData(int offset)
|
||||
void QMapDataBase::freeData(QMapDataBase *d)
|
||||
{
|
||||
Node *e = reinterpret_cast<Node *>(this);
|
||||
Node *cur = e->forward[0];
|
||||
Node *prev;
|
||||
while (cur != e) {
|
||||
prev = cur;
|
||||
cur = cur->forward[0];
|
||||
if (strictAlignment)
|
||||
qFreeAligned(reinterpret_cast<char *>(prev) - offset);
|
||||
else
|
||||
free(reinterpret_cast<char *>(prev) - offset);
|
||||
}
|
||||
delete this;
|
||||
delete d;
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new node inside the data structure.
|
||||
|
||||
\a update is an array with pointers to the node after which the new node
|
||||
should be inserted. Because of the strange skip list data structure there
|
||||
could be several pointers to this node on different levels.
|
||||
\a offset is an amount of bytes that needs to reserved just before the
|
||||
QMapData::Node structure.
|
||||
|
||||
\a alignment dictates the alignment for the data.
|
||||
|
||||
\internal
|
||||
\since 4.6
|
||||
*/
|
||||
QMapData::Node *QMapData::node_create(Node *update[], int offset, int alignment)
|
||||
{
|
||||
int level = 0;
|
||||
uint mask = (1 << Sparseness) - 1;
|
||||
|
||||
while ((randomBits & mask) == mask && level < LastLevel) {
|
||||
++level;
|
||||
mask <<= Sparseness;
|
||||
}
|
||||
|
||||
if (level > topLevel) {
|
||||
Node *e = reinterpret_cast<Node *>(this);
|
||||
level = ++topLevel;
|
||||
e->forward[level] = e;
|
||||
update[level] = e;
|
||||
}
|
||||
|
||||
++randomBits;
|
||||
if (level == 3 && !insertInOrder)
|
||||
randomBits = qrand();
|
||||
|
||||
void *concreteNode = strictAlignment ?
|
||||
qMallocAligned(offset + sizeof(Node) + level * sizeof(Node *), alignment) :
|
||||
malloc(offset + sizeof(Node) + level * sizeof(Node *));
|
||||
Q_CHECK_PTR(concreteNode);
|
||||
|
||||
Node *abstractNode = reinterpret_cast<Node *>(reinterpret_cast<char *>(concreteNode) + offset);
|
||||
|
||||
abstractNode->backward = update[0];
|
||||
update[0]->forward[0]->backward = abstractNode;
|
||||
|
||||
for (int i = level; i >= 0; i--) {
|
||||
abstractNode->forward[i] = update[i]->forward[i];
|
||||
update[i]->forward[i] = abstractNode;
|
||||
update[i] = abstractNode;
|
||||
}
|
||||
++size;
|
||||
return abstractNode;
|
||||
}
|
||||
|
||||
void QMapData::node_delete(Node *update[], int offset, Node *node)
|
||||
{
|
||||
node->forward[0]->backward = node->backward;
|
||||
|
||||
for (int i = 0; i <= topLevel; ++i) {
|
||||
if (update[i]->forward[i] != node)
|
||||
break;
|
||||
update[i]->forward[i] = node->forward[i];
|
||||
}
|
||||
--size;
|
||||
if (strictAlignment)
|
||||
qFreeAligned(reinterpret_cast<char *>(node) - offset);
|
||||
else
|
||||
free(reinterpret_cast<char *>(node) - offset);
|
||||
}
|
||||
|
||||
#ifdef QT_QMAP_DEBUG
|
||||
|
||||
uint QMapData::adjust_ptr(Node *node)
|
||||
{
|
||||
if (node == reinterpret_cast<Node *>(this)) {
|
||||
return (uint)0xDEADBEEF;
|
||||
} else {
|
||||
return (uint)node;
|
||||
}
|
||||
}
|
||||
|
||||
void QMapData::dump()
|
||||
{
|
||||
qDebug("Map data (ref = %d, size = %d, randomBits = %#.8x)", int(ref), size, randomBits);
|
||||
|
||||
QString preOutput;
|
||||
QVector<QString> output(topLevel + 1);
|
||||
Node *e = reinterpret_cast<Node *>(this);
|
||||
|
||||
QString str;
|
||||
str.sprintf(" %.8x", adjust_ptr(reinterpret_cast<Node *>(this)));
|
||||
preOutput += str;
|
||||
|
||||
Node *update[LastLevel + 1];
|
||||
for (int i = 0; i <= topLevel; ++i) {
|
||||
str.sprintf("%d: [%.8x] -", i, adjust_ptr(reinterpret_cast<Node *>(forward[i])));
|
||||
output[i] += str;
|
||||
update[i] = reinterpret_cast<Node *>(forward[i]);
|
||||
}
|
||||
|
||||
Node *node = reinterpret_cast<Node *>(forward[0]);
|
||||
while (node != e) {
|
||||
int level = 0;
|
||||
while (level < topLevel && update[level + 1] == node)
|
||||
++level;
|
||||
|
||||
str.sprintf(" %.8x", adjust_ptr(node));
|
||||
preOutput += str;
|
||||
|
||||
for (int i = 0; i <= level; ++i) {
|
||||
str.sprintf("-> [%.8x] -", adjust_ptr(node->forward[i]));
|
||||
output[i] += str;
|
||||
update[i] = node->forward[i];
|
||||
}
|
||||
for (int j = level + 1; j <= topLevel; ++j)
|
||||
output[j] += QLatin1String("---------------");
|
||||
node = node->forward[0];
|
||||
}
|
||||
|
||||
qDebug("%s", preOutput.ascii());
|
||||
for (int i = 0; i <= topLevel; ++i)
|
||||
qDebug("%s", output[i].ascii());
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\class QMap
|
||||
\brief The QMap class is a template class that provides a skip-list-based dictionary.
|
||||
@ -482,11 +628,6 @@ void QMapData::dump()
|
||||
\internal
|
||||
*/
|
||||
|
||||
/*! \fn void QMap::setInsertInOrder(bool sharable)
|
||||
|
||||
\internal
|
||||
*/
|
||||
|
||||
/*! \fn void QMap::clear()
|
||||
|
||||
Removes all items from the map.
|
||||
@ -529,22 +670,21 @@ void QMapData::dump()
|
||||
|
||||
/*! \fn const T QMap::value(const Key &key) const
|
||||
|
||||
Returns the value associated with the key \a key.
|
||||
|
||||
If the map contains no item with key \a key, the function
|
||||
returns a \l{default-constructed value}. If there are multiple
|
||||
items for \a key in the map, the value of the most recently
|
||||
inserted one is returned.
|
||||
|
||||
\sa key(), values(), contains(), operator[]()
|
||||
*/
|
||||
|
||||
/*! \fn const T QMap::value(const Key &key, const T &defaultValue) const
|
||||
|
||||
\overload
|
||||
|
||||
Returns the value associated with the key \a key.
|
||||
|
||||
If the map contains no item with key \a key, the function returns
|
||||
\a defaultValue.
|
||||
\a defaultValue. If no \a defaultValue is specified, the function
|
||||
returns a \l{default-constructed value}. If there are multiple
|
||||
items for \a key in the map, the value of the most recently
|
||||
inserted one is returned.
|
||||
|
||||
\sa key(), values(), contains(), operator[]()
|
||||
*/
|
||||
|
||||
/*! \fn T &QMap::operator[](const Key &key)
|
||||
@ -606,32 +746,21 @@ void QMapData::dump()
|
||||
by value.
|
||||
*/
|
||||
|
||||
/*! \fn Key QMap::key(const T &value) const
|
||||
|
||||
Returns the first key with value \a value.
|
||||
|
||||
If the map contains no item with value \a value, the function
|
||||
returns a \link {default-constructed value} default-constructed
|
||||
key \endlink.
|
||||
|
||||
This function can be slow (\l{linear time}), because QMap's
|
||||
internal data structure is optimized for fast lookup by key, not
|
||||
by value.
|
||||
|
||||
\sa value(), keys()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn Key QMap::key(const T &value, const Key &defaultKey) const
|
||||
\since 4.3
|
||||
\overload
|
||||
|
||||
Returns the first key with value \a value, or \a defaultKey if
|
||||
the map contains no item with value \a value.
|
||||
the map contains no item with value \a value. If no \a defaultKey
|
||||
is provided the function returns a \link {default-constructed value}
|
||||
default-constructed key \endlink.
|
||||
|
||||
This function can be slow (\l{linear time}), because QMap's
|
||||
internal data structure is optimized for fast lookup by key, not
|
||||
by value.
|
||||
|
||||
\sa value(), keys()
|
||||
*/
|
||||
|
||||
/*! \fn QList<T> QMap::values() const
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -41,10 +41,10 @@
|
||||
|
||||
#define QT_STRICT_ITERATORS
|
||||
|
||||
#include <qmap.h>
|
||||
#include <QtTest/QtTest>
|
||||
#include <QDebug>
|
||||
|
||||
#include <qmap.h>
|
||||
|
||||
class tst_QMap : public QObject
|
||||
{
|
||||
@ -74,6 +74,11 @@ private slots:
|
||||
void qmultimap_specific();
|
||||
|
||||
void const_shared_null();
|
||||
|
||||
void equal_range();
|
||||
void setSharable();
|
||||
|
||||
void insert();
|
||||
};
|
||||
|
||||
typedef QMap<QString, QString> StringMap;
|
||||
@ -105,6 +110,11 @@ int MyClass::count = 0;
|
||||
|
||||
typedef QMap<QString, MyClass> MyMap;
|
||||
|
||||
QDebug operator << (QDebug d, const MyClass &c) {
|
||||
d << c.str;
|
||||
return d;
|
||||
}
|
||||
|
||||
void tst_QMap::init()
|
||||
{
|
||||
MyClass::count = 0;
|
||||
@ -152,6 +162,7 @@ void tst_QMap::count()
|
||||
map.insert( "Paul", MyClass("Tvete 6") );
|
||||
|
||||
QCOMPARE( map.count(), 9 );
|
||||
QCOMPARE( map.count("Paul"), 1 );
|
||||
#ifndef Q_CC_SUN
|
||||
QCOMPARE( MyClass::count, 9 );
|
||||
#endif
|
||||
@ -519,6 +530,7 @@ void tst_QMap::find()
|
||||
for(i = 3; i < 10; ++i) {
|
||||
compareString = testString.arg(i);
|
||||
map1.insertMulti(4, compareString);
|
||||
QCOMPARE(map1.count(4), i - 2);
|
||||
}
|
||||
|
||||
QMap<int, QString>::const_iterator it=map1.constFind(4);
|
||||
@ -588,6 +600,33 @@ void tst_QMap::lowerUpperBound()
|
||||
QCOMPARE(map1.lowerBound(2).key(), 5); // returns iterator to (5, "five")
|
||||
QCOMPARE(map1.lowerBound(10).key(), 10); // returns iterator to (10, "ten")
|
||||
QVERIFY(map1.lowerBound(999) == map1.end()); // returns end()
|
||||
|
||||
map1.insert(3, "three");
|
||||
map1.insert(7, "seven");
|
||||
map1.insertMulti(7, "seven_2");
|
||||
|
||||
QCOMPARE(map1.upperBound(0).key(), 1);
|
||||
QCOMPARE(map1.upperBound(1).key(), 3);
|
||||
QCOMPARE(map1.upperBound(2).key(), 3);
|
||||
QCOMPARE(map1.upperBound(3).key(), 5);
|
||||
QCOMPARE(map1.upperBound(7).key(), 10);
|
||||
QVERIFY(map1.upperBound(10) == map1.end());
|
||||
QVERIFY(map1.upperBound(999) == map1.end());
|
||||
|
||||
QCOMPARE(map1.lowerBound(0).key(), 1);
|
||||
QCOMPARE(map1.lowerBound(1).key(), 1);
|
||||
QCOMPARE(map1.lowerBound(2).key(), 3);
|
||||
QCOMPARE(map1.lowerBound(3).key(), 3);
|
||||
QCOMPARE(map1.lowerBound(4).key(), 5);
|
||||
QCOMPARE(map1.lowerBound(5).key(), 5);
|
||||
QCOMPARE(map1.lowerBound(6).key(), 7);
|
||||
QCOMPARE(map1.lowerBound(7).key(), 7);
|
||||
QCOMPARE(map1.lowerBound(6).value(), QString("seven_2"));
|
||||
QCOMPARE(map1.lowerBound(7).value(), QString("seven_2"));
|
||||
QCOMPARE((++map1.lowerBound(6)).value(), QString("seven"));
|
||||
QCOMPARE((++map1.lowerBound(7)).value(), QString("seven"));
|
||||
QCOMPARE(map1.lowerBound(10).key(), 10);
|
||||
QVERIFY(map1.lowerBound(999) == map1.end());
|
||||
}
|
||||
|
||||
void tst_QMap::mergeCompare()
|
||||
@ -846,10 +885,129 @@ void tst_QMap::const_shared_null()
|
||||
QMap<int, QString> map2;
|
||||
map2.setSharable(true);
|
||||
QVERIFY(!map2.isDetached());
|
||||
}
|
||||
|
||||
QMap<int, QString> map3;
|
||||
map3.setInsertInOrder(true);
|
||||
map3.setInsertInOrder(false);
|
||||
void tst_QMap::equal_range()
|
||||
{
|
||||
QMap<int, QString> map;
|
||||
|
||||
QPair<QMap<int, QString>::iterator, QMap<int, QString>::iterator> result = map.equal_range(0);
|
||||
QCOMPARE(result.first, map.end());
|
||||
QCOMPARE(result.second, map.end());
|
||||
|
||||
map.insert(1, "one");
|
||||
|
||||
result = map.equal_range(0);
|
||||
QCOMPARE(result.first, map.find(1));
|
||||
QCOMPARE(result.second, map.find(1));
|
||||
|
||||
result = map.equal_range(1);
|
||||
QCOMPARE(result.first, map.find(1));
|
||||
QCOMPARE(result.second, map.end());
|
||||
|
||||
result = map.equal_range(2);
|
||||
QCOMPARE(result.first, map.end());
|
||||
QCOMPARE(result.second, map.end());
|
||||
|
||||
for (int i = -10; i < 10; i += 2)
|
||||
map.insert(i, QString("%1").arg(i));
|
||||
|
||||
result = map.equal_range(0);
|
||||
QCOMPARE(result.first, map.find(0));
|
||||
QCOMPARE(result.second, map.find(1));
|
||||
|
||||
result = map.equal_range(1);
|
||||
QCOMPARE(result.first, map.find(1));
|
||||
QCOMPARE(result.second, map.find(2));
|
||||
|
||||
result = map.equal_range(2);
|
||||
QCOMPARE(result.first, map.find(2));
|
||||
QCOMPARE(result.second, map.find(4));
|
||||
|
||||
map.insertMulti(1, "another one");
|
||||
result = map.equal_range(1);
|
||||
QCOMPARE(result.first, map.find(1));
|
||||
QCOMPARE(result.second, map.find(2));
|
||||
|
||||
QCOMPARE(map.count(1), 2);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
const T &const_(const T &t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
void tst_QMap::setSharable()
|
||||
{
|
||||
QMap<int, QString> map;
|
||||
|
||||
map.insert(1, "um");
|
||||
map.insert(2, "dois");
|
||||
map.insert(4, "quatro");
|
||||
map.insert(5, "cinco");
|
||||
|
||||
map.setSharable(true);
|
||||
QCOMPARE(map.size(), 4);
|
||||
QCOMPARE(const_(map)[4], QString("quatro"));
|
||||
|
||||
{
|
||||
QMap<int, QString> copy(map);
|
||||
|
||||
QVERIFY(!map.isDetached());
|
||||
QVERIFY(copy.isSharedWith(map));
|
||||
}
|
||||
|
||||
map.setSharable(false);
|
||||
QVERIFY(map.isDetached());
|
||||
QCOMPARE(map.size(), 4);
|
||||
QCOMPARE(const_(map)[4], QString("quatro"));
|
||||
|
||||
{
|
||||
QMap<int, QString> copy(map);
|
||||
|
||||
QVERIFY(map.isDetached());
|
||||
QVERIFY(copy.isDetached());
|
||||
|
||||
QCOMPARE(copy.size(), 4);
|
||||
QCOMPARE(const_(copy)[4], QString("quatro"));
|
||||
|
||||
QCOMPARE(map, copy);
|
||||
}
|
||||
|
||||
map.setSharable(true);
|
||||
QCOMPARE(map.size(), 4);
|
||||
QCOMPARE(const_(map)[4], QString("quatro"));
|
||||
|
||||
{
|
||||
QMap<int, QString> copy(map);
|
||||
|
||||
QVERIFY(!map.isDetached());
|
||||
QVERIFY(copy.isSharedWith(map));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMap::insert()
|
||||
{
|
||||
QMap<QString, float> map;
|
||||
map.insert("cs/key1", 1);
|
||||
map.insert("cs/key2", 2);
|
||||
map.insert("cs/key1", 3);
|
||||
QCOMPARE(map.count(), 2);
|
||||
|
||||
QMap<int, int> intMap;
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
intMap.insert(i, i);
|
||||
}
|
||||
|
||||
QCOMPARE(intMap.size(), 1000);
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
QCOMPARE(intMap.value(i), i);
|
||||
intMap.insert(i, -1);
|
||||
QCOMPARE(intMap.size(), 1000);
|
||||
QCOMPARE(intMap.value(i), -1);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QMap)
|
||||
|
165
tests/benchmarks/corelib/tools/qmap/main.cpp
Normal file
165
tests/benchmarks/corelib/tools/qmap/main.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** $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 <QFile>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QTest>
|
||||
#include <qdebug.h>
|
||||
|
||||
|
||||
class tst_QMap : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void insertion_int_int();
|
||||
void insertion_int_string();
|
||||
void insertion_string_int();
|
||||
|
||||
void lookup_int_int();
|
||||
void lookup_int_string();
|
||||
void lookup_string_int();
|
||||
|
||||
void iteration();
|
||||
};
|
||||
|
||||
|
||||
void tst_QMap::insertion_int_int()
|
||||
{
|
||||
QMap<int, int> map;
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
map.insert(i, i);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMap::insertion_int_string()
|
||||
{
|
||||
QMap<int, QString> map;
|
||||
QString str("Hello World");
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
map.insert(i, str);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMap::insertion_string_int()
|
||||
{
|
||||
QMap<QString, int> map;
|
||||
QString str("Hello World");
|
||||
QBENCHMARK {
|
||||
for (int i = 1; i < 100000; ++i) {
|
||||
str[0] = QChar(i);
|
||||
map.insert(str, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tst_QMap::lookup_int_int()
|
||||
{
|
||||
QMap<int, int> map;
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
map.insert(i, i);
|
||||
|
||||
int sum = 0;
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
sum += map.value(i);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMap::lookup_int_string()
|
||||
{
|
||||
QMap<int, QString> map;
|
||||
QString str("Hello World");
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
map.insert(i, str);
|
||||
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
str += map.value(i);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMap::lookup_string_int()
|
||||
{
|
||||
QMap<QString, int> map;
|
||||
QString str("Hello World");
|
||||
for (int i = 1; i < 100000; ++i) {
|
||||
str[0] = QChar(i);
|
||||
map.insert(str, i);
|
||||
}
|
||||
|
||||
int sum = 0;
|
||||
QBENCHMARK {
|
||||
for (int i = 1; i < 100000; ++i) {
|
||||
str[0] = QChar(i);
|
||||
sum += map.value(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iteration speed doesn't depend on the type of the map.
|
||||
void tst_QMap::iteration()
|
||||
{
|
||||
QMap<int, int> map;
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
map.insert(i, i);
|
||||
|
||||
int j = 0;
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
QMap<int, int>::const_iterator it = map.constBegin();
|
||||
QMap<int, int>::const_iterator end = map.constEnd();
|
||||
while (it != end) {
|
||||
j += *it;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QMap)
|
||||
|
||||
#include "main.moc"
|
5
tests/benchmarks/corelib/tools/qmap/qmap.pro
Normal file
5
tests/benchmarks/corelib/tools/qmap/qmap.pro
Normal file
@ -0,0 +1,5 @@
|
||||
TARGET = tst_qmap
|
||||
QT = core testlib
|
||||
INCLUDEPATH += .
|
||||
SOURCES += main.cpp
|
||||
CONFIG += release
|
@ -5,6 +5,7 @@ SUBDIRS = \
|
||||
qbytearray \
|
||||
qcontiguouscache \
|
||||
qlist \
|
||||
qmap \
|
||||
qrect \
|
||||
qregexp \
|
||||
qstring \
|
||||
|
Loading…
Reference in New Issue
Block a user