Fix capacity reservation for shared QByteArray

We can squeeze, but not by discarding elements. Make sure the size of
the object stays intact after changing the reserved capacity.

I've also added unit tests for other containers, just to be sure.

Task-number: QTBUG-37750
Change-Id: I5135b095943b7589423c51cebcb52af792468e61
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com>
This commit is contained in:
Thiago Macieira 2014-03-24 12:53:27 -07:00 committed by The Qt Project
parent 3e930baa98
commit f1540a2966
5 changed files with 54 additions and 1 deletions

View File

@ -462,7 +462,7 @@ inline int QByteArray::capacity() const
inline void QByteArray::reserve(int asize)
{
if (d->ref.isShared() || uint(asize) + 1u > d->alloc) {
reallocData(uint(asize) + 1u, d->detachFlags() | Data::CapacityReserved);
reallocData(qMax(uint(size()), uint(asize)) + 1u, d->detachFlags() | Data::CapacityReserved);
} else {
// cannot set unconditionally, since d could be the shared_null or
// otherwise static

View File

@ -1855,6 +1855,21 @@ void tst_QByteArray::reserve()
QVERIFY(data == qba.data());
}
qba.resize(capacity);
QByteArray copy = qba;
qba.reserve(capacity / 2);
QCOMPARE(qba.size(), capacity); // we didn't shrink the size!
QCOMPARE(qba.capacity(), capacity);
QCOMPARE(copy.capacity(), capacity);
copy = qba;
qba.reserve(capacity * 2);
QCOMPARE(qba.size(), capacity);
QCOMPARE(qba.capacity(), capacity * 2);
QCOMPARE(copy.capacity(), capacity);
QVERIFY(qba.constData() != data);
QByteArray nil1, nil2;
nil1.reserve(0);
nil2.squeeze();

View File

@ -278,6 +278,8 @@ private slots:
void setSharableComplex() const;
void eraseValidIteratorsOnSharedList() const;
void insertWithValidIteratorsOnSharedList() const;
void reserve() const;
private:
template<typename T> void length() const;
template<typename T> void append() const;
@ -1669,5 +1671,31 @@ void tst_QList::insertWithValidIteratorsOnSharedList() const
QCOMPARE(a.at(1), 15);
}
void tst_QList::reserve() const
{
// Note:
// This test depends on QList's current behavior that ints are stored in the array itself.
// This test would not work for QList<Complex>.
int capacity = 100;
QList<int> list;
list.reserve(capacity);
list << 0;
int *data = &list[0];
for (int i = 1; i < capacity; i++) {
list << i;
QCOMPARE(&list.at(0), data);
}
QList<int> copy = list;
list.reserve(capacity / 2);
QCOMPARE(list.size(), capacity); // we didn't shrink the size!
copy = list;
list.reserve(capacity * 2);
QCOMPARE(list.size(), capacity);
QVERIFY(&list.at(0) != data);
}
QTEST_APPLESS_MAIN(tst_QList)
#include "tst_qlist.moc"

View File

@ -4277,14 +4277,23 @@ void tst_QString::capacity()
QVERIFY( (int)s2.capacity() >= res );
QCOMPARE( s2, s1 );
s2 = s1; // share again
s2.reserve( res * 2 );
QVERIFY( (int)s2.capacity() >= res * 2 );
QVERIFY(s2.constData() != s1.constData());
QCOMPARE( s2, s1 );
// don't share again -- s2 must be detached for squeeze() to do anything
s2.squeeze();
QVERIFY( (int)s2.capacity() == res );
QCOMPARE( s2, s1 );
s2 = s1; // share again
int oldsize = s1.size();
s2.reserve( res / 2 );
QVERIFY( (int)s2.capacity() >= res / 2 );
QVERIFY( (int)s2.capacity() >= oldsize );
QCOMPARE( s2, s1 );
}
void tst_QString::section_data()

View File

@ -1942,6 +1942,7 @@ void tst_QVector::reserve()
a.resize(2);
QVector<Foo> b(a);
b.reserve(1);
QCOMPARE(b.size(), a.size());
}
QCOMPARE(fooCtor, fooDtor);
}