Add AllocateOptions to QArrayData

This approach is better for future ABI evolution than using individual
bool parameters. QArrayData now also offers to calculate allocate
options for typical detach and clone operations: the CapacityReserved
flag is preserved, while cloning resets the Unsharable state.

Change-Id: I256e135adcf27a52a5c7d6130069c35c8b946bc3
Reviewed-by: João Abecasis <joao.abecasis@nokia.com>
This commit is contained in:
João Abecasis 2011-11-22 17:28:14 +01:00 committed by Qt by Nokia
parent d91b4f0b13
commit f1e48d48fd
5 changed files with 78 additions and 33 deletions

View File

@ -49,7 +49,7 @@ static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0
static const QArrayData qt_array_unsharable_empty = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 };
QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
size_t capacity, bool reserve, bool sharable)
size_t capacity, AllocateOptions options)
{
// Alignment is a power of two
Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)
@ -57,7 +57,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
// Don't allocate empty headers
if (!capacity)
return sharable
return !(options & Unsharable)
? const_cast<QArrayData *>(&qt_array_empty)
: const_cast<QArrayData *>(&qt_array_unsharable_empty);
@ -73,10 +73,10 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
& ~(alignment - 1);
header->ref.atomic.store(sharable ? 1 : 0);
header->ref.atomic.store(bool(!(options & Unsharable)));
header->size = 0;
header->alloc = capacity;
header->capacityReserved = reserve;
header->capacityReserved = bool(options & CapacityReserved);
header->offset = data - quintptr(header);
}

View File

@ -73,14 +73,43 @@ struct Q_CORE_EXPORT QArrayData
return reinterpret_cast<const char *>(this) + offset;
}
enum AllocateOption {
CapacityReserved = 0x1,
Unsharable = 0x2,
Default = 0
};
Q_DECLARE_FLAGS(AllocateOptions, AllocateOption)
AllocateOptions detachFlags() const
{
AllocateOptions result;
if (!ref.isSharable())
result |= Unsharable;
if (capacityReserved)
result |= CapacityReserved;
return result;
}
AllocateOptions cloneFlags() const
{
AllocateOptions result;
if (capacityReserved)
result |= CapacityReserved;
return result;
}
static QArrayData *allocate(size_t objectSize, size_t alignment,
size_t capacity, bool reserve, bool sharable) Q_REQUIRED_RESULT;
size_t capacity, AllocateOptions options = Default) Q_REQUIRED_RESULT;
static void deallocate(QArrayData *data, size_t objectSize,
size_t alignment);
static const QArrayData shared_null;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocateOptions)
template <class T>
struct QTypedArrayData
: QArrayData
@ -98,11 +127,11 @@ struct QTypedArrayData
class AlignmentDummy { QArrayData header; T data; };
static QTypedArrayData *allocate(size_t capacity, bool reserve = false,
bool sharable = true) Q_REQUIRED_RESULT
static QTypedArrayData *allocate(size_t capacity,
AllocateOptions options = Default) Q_REQUIRED_RESULT
{
return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T),
Q_ALIGNOF(AlignmentDummy), capacity, reserve, sharable));
Q_ALIGNOF(AlignmentDummy), capacity, options));
}
static void deallocate(QArrayData *data)

View File

@ -66,7 +66,7 @@ public:
QArrayDataPointer(const QArrayDataPointer &other)
: d(other.d->ref.ref()
? other.d
: other.clone())
: other.clone(other.d->cloneFlags()))
{
}
@ -115,7 +115,9 @@ public:
void setSharable(bool sharable)
{
if (d->alloc == 0 && d->size == 0) {
d = Data::allocate(0, false, sharable);
d = Data::allocate(0, sharable
? QArrayData::Default
: QArrayData::Unsharable);
return;
}
@ -137,7 +139,7 @@ public:
bool detach()
{
if (d->ref.isShared()) {
Data *copy = clone();
Data *copy = clone(d->detachFlags());
QArrayDataPointer old(d);
d = copy;
return true;
@ -147,10 +149,10 @@ public:
}
private:
Data *clone() const Q_REQUIRED_RESULT
Data *clone(QArrayData::AllocateOptions options) const Q_REQUIRED_RESULT
{
QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size,
d->capacityReserved));
options));
if (d->size)
copy->copyAppend(d->begin(), d->end());

View File

@ -140,7 +140,8 @@ public:
|| (n
&& !d->capacityReserved
&& (d->ref != 1 || (d->capacityReserved = 1, false)))) {
SimpleVector detached(Data::allocate(n, true));
SimpleVector detached(Data::allocate(n,
d->detachFlags() | Data::CapacityReserved));
detached.d->copyAppend(constBegin(), constEnd());
detached.swap(*this);
}
@ -160,7 +161,8 @@ public:
if (d->ref != 1
|| capacity() - size() < size_t(last - first)) {
SimpleVector detached(Data::allocate(
qMax(capacity(), size() + (last - first)), d->capacityReserved));
qMax(capacity(), size() + (last - first)),
d->detachFlags()));
detached.d->copyAppend(first, last);
detached.d->copyAppend(begin, begin + d->size);
@ -180,7 +182,8 @@ public:
if (d->ref != 1
|| capacity() - size() < size_t(last - first)) {
SimpleVector detached(Data::allocate(
qMax(capacity(), size() + (last - first)), d->capacityReserved));
qMax(capacity(), size() + (last - first)),
d->detachFlags()));
if (d->size) {
const T *const begin = constBegin();
@ -219,7 +222,8 @@ public:
if (d->ref != 1
|| capacity() - size() < size_t(last - first)) {
SimpleVector detached(Data::allocate(
qMax(capacity(), size() + (last - first)), d->capacityReserved));
qMax(capacity(), size() + (last - first)),
d->detachFlags()));
if (position)
detached.d->copyAppend(begin, where);

View File

@ -153,7 +153,7 @@ void tst_QArrayData::referenceCounting()
void tst_QArrayData::sharedNullEmpty()
{
QArrayData *null = const_cast<QArrayData *>(&QArrayData::shared_null);
QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0, false, true);
QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0);
QVERIFY(null->ref.isStatic());
QVERIFY(null->ref.isSharable());
@ -473,11 +473,13 @@ struct Deallocator
};
Q_DECLARE_METATYPE(const QArrayData *)
Q_DECLARE_METATYPE(QArrayData::AllocateOptions)
void tst_QArrayData::allocate_data()
{
QTest::addColumn<size_t>("objectSize");
QTest::addColumn<size_t>("alignment");
QTest::addColumn<QArrayData::AllocateOptions>("allocateOptions");
QTest::addColumn<bool>("isCapacityReserved");
QTest::addColumn<bool>("isSharable");
QTest::addColumn<const QArrayData *>("commonEmpty");
@ -492,22 +494,25 @@ void tst_QArrayData::allocate_data()
{ "void *", sizeof(void *), Q_ALIGNOF(void *) }
};
QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, true);
QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, false);
QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0);
QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable);
QVERIFY(shared_empty);
QVERIFY(unsharable_empty);
struct {
char const *description;
QArrayData::AllocateOptions allocateOptions;
bool isCapacityReserved;
bool isSharable;
const QArrayData *commonEmpty;
} options[] = {
{ "Default", false, true, shared_empty },
{ "Reserved", true, true, shared_empty },
{ "Reserved | Unsharable", true, false, unsharable_empty },
{ "Unsharable", false, false, unsharable_empty },
{ "Default", QArrayData::Default, false, true, shared_empty },
{ "Reserved", QArrayData::CapacityReserved, true, true, shared_empty },
{ "Reserved | Unsharable",
QArrayData::CapacityReserved | QArrayData::Unsharable, true, false,
unsharable_empty },
{ "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty },
};
for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
@ -517,14 +522,15 @@ void tst_QArrayData::allocate_data()
+ QLatin1String(": ")
+ QLatin1String(options[j].description)))
<< types[i].objectSize << types[i].alignment
<< options[j].isCapacityReserved << options[j].isSharable
<< options[j].commonEmpty;
<< options[j].allocateOptions << options[j].isCapacityReserved
<< options[j].isSharable << options[j].commonEmpty;
}
void tst_QArrayData::allocate()
{
QFETCH(size_t, objectSize);
QFETCH(size_t, alignment);
QFETCH(QArrayData::AllocateOptions, allocateOptions);
QFETCH(bool, isCapacityReserved);
QFETCH(bool, isSharable);
QFETCH(const QArrayData *, commonEmpty);
@ -535,14 +541,14 @@ void tst_QArrayData::allocate()
// Shared Empty
QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0,
isCapacityReserved, isSharable), commonEmpty);
QArrayData::AllocateOptions(allocateOptions)), 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, isSharable);
capacity, QArrayData::AllocateOptions(allocateOptions));
keeper.headers.append(data);
QCOMPARE(data->size, 0);
@ -584,7 +590,7 @@ void tst_QArrayData::alignment()
for (int i = 0; i < 100; ++i) {
QArrayData *data = QArrayData::allocate(sizeof(Unaligned),
minAlignment, 8, false, true);
minAlignment, 8, QArrayData::Default);
keeper.headers.append(data);
QVERIFY(data);
@ -952,10 +958,14 @@ void tst_QArrayData::setSharable_data()
{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
};
QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5, true, true));
QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10, false, true));
QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15, true, true));
QArrayDataPointer<int> staticArray(static_cast<QTypedArrayData<int> *>(&staticArrayData.header));
QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5,
QArrayData::CapacityReserved));
QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10,
QArrayData::Default));
QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15,
QArrayData::CapacityReserved));
QArrayDataPointer<int> staticArray(
static_cast<QTypedArrayData<int> *>(&staticArrayData.header));
nonEmpty->copyAppend(5, 1);
nonEmptyReserved->copyAppend(7, 2);