qAllocMore: Always grow exponentially

qAllocMore is used by growing containers to allocate additional memory
for future growth. The previous algorithm would grow linearly in
increments of 8 up to 64 and then progress exponentially in powers of
two.

The new (constant time) algorithm does away with the linear segment and
always progresses exponentially. It also has the nice benefit of cleanly
avoiding undefined behaviour that the old implementation tried hard to
circumvent.

Besides always progressing exponentially, the next-power-of-two
algorithm was tweaked to always include space for growth. Previously
queries at boundary values (powers of two) would return the same value.

The test was updated to verify sanity of results. As the algorithm is
well behaved, testing of bogus data was dropped. Whatever happens in
those cases is irrelevant, anyway: the bug lives elsewhere.

Change-Id: I4def473cce4b438734887084e3c3bd8da0ff466b
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
This commit is contained in:
João Abecasis 2012-02-16 19:59:07 +01:00 committed by Qt by Nokia
parent 5d593da3d3
commit fb8be9905d
2 changed files with 37 additions and 29 deletions

View File

@ -69,24 +69,25 @@ int qFindByteArray(
int qAllocMore(int alloc, int extra)
{
if (alloc == 0 && extra == 0)
return 0;
const int page = 1 << 12;
int nalloc;
alloc += extra;
if (alloc < 1<<6) {
nalloc = (1<<3) + ((alloc >>3) << 3);
} else {
// don't do anything if the loop will overflow signed int.
if (alloc >= INT_MAX/2)
return INT_MAX;
nalloc = (alloc < page) ? 1 << 3 : page;
while (nalloc < alloc) {
if (nalloc <= 0)
return INT_MAX;
nalloc *= 2;
}
}
Q_ASSERT(alloc >= 0 && extra >= 0);
Q_ASSERT(alloc < (1 << 30) - extra);
unsigned nalloc = alloc + extra;
// Round up to next power of 2
// Assuming container is growing, always overshoot
//--nalloc;
nalloc |= nalloc >> 1;
nalloc |= nalloc >> 2;
nalloc |= nalloc >> 4;
nalloc |= nalloc >> 8;
nalloc |= nalloc >> 16;
++nalloc;
Q_ASSERT(nalloc > unsigned(alloc + extra));
return nalloc - extra;
}

View File

@ -1075,18 +1075,25 @@ void tst_QByteArray::toULongLong()
// global function defined in qbytearray.cpp
void tst_QByteArray::qAllocMore()
{
static const int t[] = {
INT_MIN, INT_MIN + 1, -1234567, -66000, -1025,
-3, -1, 0, +1, +3, +1025, +66000, +1234567, INT_MAX - 1, INT_MAX,
INT_MAX/3
};
static const int N = sizeof(t)/sizeof(t[0]);
using QT_PREPEND_NAMESPACE(qAllocMore);
// make sure qAllocMore() doesn't loop infinitely on any input
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
::qAllocMore(t[i], t[j]);
}
// Not very important, but please behave :-)
QVERIFY(qAllocMore(0, 0) >= 0);
for (int i = 1; i < 1 << 8; i <<= 1)
QVERIFY(qAllocMore(i, 0) >= i);
for (int i = 1 << 8; i < 1 << 30; i <<= 1) {
const int alloc = qAllocMore(i, 0);
QVERIFY(alloc >= i);
QCOMPARE(qAllocMore(i - 8, 8), alloc - 8);
QCOMPARE(qAllocMore(i - 16, 16), alloc - 16);
QCOMPARE(qAllocMore(i - 24, 24), alloc - 24);
QCOMPARE(qAllocMore(i - 32, 32), alloc - 32);
QVERIFY(qAllocMore(i - 1, 0) >= i - 1);
QVERIFY(qAllocMore(i + 1, 0) >= i + 1);
}
}