Allocate/free support in QArrayData
Centralizing QArrayData memory management decisions in one place will allow us to be smarter in how we allocate header and data. At the moment, these are allocated as a single block. In the future we may decide to allocate them separately for "large" data or specific alignment requirements. For users of QArrayData this remains transparent and not part of the ABI. The offset field in QArrayDataHeader enables this. This also hard-wires allocation of empty arrays to return shared_empty. Allocating detached headers (e.g., to support fromRawData) will thus require explicit support. Change-Id: Icac5a1f51ee7e468c76b4493d29debc18780e5dc Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
This commit is contained in:
parent
4714f86c54
commit
0806bc2d1b
@ -46,4 +46,48 @@ QT_BEGIN_NAMESPACE
|
||||
const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 };
|
||||
const QArrayData QArrayData::shared_empty = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, 0 };
|
||||
|
||||
QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
|
||||
size_t capacity, bool reserve)
|
||||
{
|
||||
// Alignment is a power of two
|
||||
Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)
|
||||
&& !(alignment & (alignment - 1)));
|
||||
|
||||
// Don't allocate empty headers
|
||||
if (!capacity)
|
||||
return const_cast<QArrayData *>(&shared_empty);
|
||||
|
||||
// Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we
|
||||
// can properly align the data array. This assumes malloc is able to
|
||||
// provide appropriate alignment for the header -- as it should!
|
||||
size_t allocSize = sizeof(QArrayData) + objectSize * capacity
|
||||
+ (alignment - Q_ALIGNOF(QArrayData));
|
||||
|
||||
QArrayData *header = static_cast<QArrayData *>(qMalloc(allocSize));
|
||||
Q_CHECK_PTR(header);
|
||||
if (header) {
|
||||
quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
|
||||
& ~(alignment - 1);
|
||||
|
||||
header->ref = 1;
|
||||
header->size = 0;
|
||||
header->alloc = capacity;
|
||||
header->capacityReserved = reserve;
|
||||
header->offset = data - quintptr(header);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
void QArrayData::deallocate(QArrayData *data, size_t objectSize,
|
||||
size_t alignment)
|
||||
{
|
||||
// Alignment is a power of two
|
||||
Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)
|
||||
&& !(alignment & (alignment - 1)));
|
||||
Q_UNUSED(objectSize) Q_UNUSED(alignment)
|
||||
|
||||
qFree(data);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -73,6 +73,11 @@ struct Q_CORE_EXPORT QArrayData
|
||||
return reinterpret_cast<const char *>(this) + offset;
|
||||
}
|
||||
|
||||
static QArrayData *allocate(size_t objectSize, size_t alignment,
|
||||
size_t capacity, bool reserve) Q_REQUIRED_RESULT;
|
||||
static void deallocate(QArrayData *data, size_t objectSize,
|
||||
size_t alignment);
|
||||
|
||||
static const QArrayData shared_null;
|
||||
static const QArrayData shared_empty;
|
||||
};
|
||||
|
@ -54,6 +54,10 @@ private slots:
|
||||
void sharedNullEmpty();
|
||||
void staticData();
|
||||
void simpleVector();
|
||||
void allocate_data();
|
||||
void allocate();
|
||||
void alignment_data();
|
||||
void alignment();
|
||||
};
|
||||
|
||||
void tst_QArrayData::referenceCounting()
|
||||
@ -253,5 +257,145 @@ void tst_QArrayData::simpleVector()
|
||||
}
|
||||
}
|
||||
|
||||
struct Deallocator
|
||||
{
|
||||
Deallocator(size_t objectSize, size_t alignment)
|
||||
: objectSize(objectSize)
|
||||
, alignment(alignment)
|
||||
{
|
||||
}
|
||||
|
||||
~Deallocator()
|
||||
{
|
||||
Q_FOREACH (QArrayData *data, headers)
|
||||
QArrayData::deallocate(data, objectSize, alignment);
|
||||
}
|
||||
|
||||
size_t objectSize;
|
||||
size_t alignment;
|
||||
QVector<QArrayData *> headers;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(const QArrayData *)
|
||||
|
||||
void tst_QArrayData::allocate_data()
|
||||
{
|
||||
QTest::addColumn<size_t>("objectSize");
|
||||
QTest::addColumn<size_t>("alignment");
|
||||
QTest::addColumn<bool>("isCapacityReserved");
|
||||
QTest::addColumn<const QArrayData *>("commonEmpty");
|
||||
|
||||
struct {
|
||||
char const *typeName;
|
||||
size_t objectSize;
|
||||
size_t alignment;
|
||||
} types[] = {
|
||||
{ "char", sizeof(char), Q_ALIGNOF(char) },
|
||||
{ "short", sizeof(short), Q_ALIGNOF(short) },
|
||||
{ "void *", sizeof(void *), Q_ALIGNOF(void *) }
|
||||
};
|
||||
|
||||
struct {
|
||||
char const *description;
|
||||
bool isCapacityReserved;
|
||||
const QArrayData *commonEmpty;
|
||||
} options[] = {
|
||||
{ "Default", false, &QArrayData::shared_empty },
|
||||
{ "Reserved", true, &QArrayData::shared_empty },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
|
||||
for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); ++j)
|
||||
QTest::newRow(qPrintable(
|
||||
QLatin1String(types[i].typeName)
|
||||
+ QLatin1String(": ")
|
||||
+ QLatin1String(options[j].description)))
|
||||
<< types[i].objectSize << types[i].alignment
|
||||
<< options[j].isCapacityReserved << options[j].commonEmpty;
|
||||
}
|
||||
|
||||
void tst_QArrayData::allocate()
|
||||
{
|
||||
QFETCH(size_t, objectSize);
|
||||
QFETCH(size_t, alignment);
|
||||
QFETCH(bool, isCapacityReserved);
|
||||
QFETCH(const QArrayData *, commonEmpty);
|
||||
|
||||
// Minimum alignment that can be requested is that of QArrayData.
|
||||
// Typically, this alignment is sizeof(void *) and ensured by malloc.
|
||||
size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData));
|
||||
|
||||
// Shared Empty
|
||||
QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0,
|
||||
isCapacityReserved), commonEmpty);
|
||||
|
||||
Deallocator keeper(objectSize, minAlignment);
|
||||
keeper.headers.reserve(1024);
|
||||
|
||||
for (int capacity = 1; capacity <= 1024; capacity <<= 1) {
|
||||
QArrayData *data = QArrayData::allocate(objectSize, minAlignment,
|
||||
capacity, isCapacityReserved);
|
||||
keeper.headers.append(data);
|
||||
|
||||
QCOMPARE(data->size, 0);
|
||||
QVERIFY(data->alloc >= uint(capacity));
|
||||
QCOMPARE(data->capacityReserved, uint(isCapacityReserved));
|
||||
|
||||
// Check that the allocated array can be used. Best tested with a
|
||||
// memory checker, such as valgrind, running.
|
||||
::memset(data->data(), 'A', objectSize * capacity);
|
||||
}
|
||||
}
|
||||
|
||||
class Unaligned
|
||||
{
|
||||
char dummy[8];
|
||||
};
|
||||
|
||||
void tst_QArrayData::alignment_data()
|
||||
{
|
||||
QTest::addColumn<size_t>("alignment");
|
||||
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
size_t alignment = 1u << i;
|
||||
QTest::newRow(qPrintable(QString::number(alignment))) << alignment;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QArrayData::alignment()
|
||||
{
|
||||
QFETCH(size_t, alignment);
|
||||
|
||||
// Minimum alignment that can be requested is that of QArrayData.
|
||||
// Typically, this alignment is sizeof(void *) and ensured by malloc.
|
||||
size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData));
|
||||
|
||||
Deallocator keeper(sizeof(Unaligned), minAlignment);
|
||||
keeper.headers.reserve(100);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
QArrayData *data = QArrayData::allocate(sizeof(Unaligned),
|
||||
minAlignment, 8, false);
|
||||
keeper.headers.append(data);
|
||||
|
||||
QVERIFY(data);
|
||||
QCOMPARE(data->size, 0);
|
||||
QVERIFY(data->alloc >= uint(8));
|
||||
|
||||
// These conditions should hold as long as header and array are
|
||||
// allocated together
|
||||
QVERIFY(data->offset >= qptrdiff(sizeof(QArrayData)));
|
||||
QVERIFY(data->offset <= qptrdiff(sizeof(QArrayData)
|
||||
+ minAlignment - Q_ALIGNOF(QArrayData)));
|
||||
|
||||
// Data is aligned
|
||||
QCOMPARE(quintptr(data->data()) % alignment, quintptr(0u));
|
||||
|
||||
// Check that the allocated array can be used. Best tested with a
|
||||
// memory checker, such as valgrind, running.
|
||||
::memset(data->data(), 'A', sizeof(Unaligned) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QArrayData)
|
||||
#include "tst_qarraydata.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user