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:
parent
d91b4f0b13
commit
f1e48d48fd
@ -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 };
|
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,
|
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
|
// Alignment is a power of two
|
||||||
Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)
|
Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)
|
||||||
@ -57,7 +57,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
|
|||||||
|
|
||||||
// Don't allocate empty headers
|
// Don't allocate empty headers
|
||||||
if (!capacity)
|
if (!capacity)
|
||||||
return sharable
|
return !(options & Unsharable)
|
||||||
? const_cast<QArrayData *>(&qt_array_empty)
|
? const_cast<QArrayData *>(&qt_array_empty)
|
||||||
: const_cast<QArrayData *>(&qt_array_unsharable_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)
|
quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
|
||||||
& ~(alignment - 1);
|
& ~(alignment - 1);
|
||||||
|
|
||||||
header->ref.atomic.store(sharable ? 1 : 0);
|
header->ref.atomic.store(bool(!(options & Unsharable)));
|
||||||
header->size = 0;
|
header->size = 0;
|
||||||
header->alloc = capacity;
|
header->alloc = capacity;
|
||||||
header->capacityReserved = reserve;
|
header->capacityReserved = bool(options & CapacityReserved);
|
||||||
header->offset = data - quintptr(header);
|
header->offset = data - quintptr(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,14 +73,43 @@ struct Q_CORE_EXPORT QArrayData
|
|||||||
return reinterpret_cast<const char *>(this) + offset;
|
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,
|
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,
|
static void deallocate(QArrayData *data, size_t objectSize,
|
||||||
size_t alignment);
|
size_t alignment);
|
||||||
|
|
||||||
static const QArrayData shared_null;
|
static const QArrayData shared_null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocateOptions)
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
struct QTypedArrayData
|
struct QTypedArrayData
|
||||||
: QArrayData
|
: QArrayData
|
||||||
@ -98,11 +127,11 @@ struct QTypedArrayData
|
|||||||
|
|
||||||
class AlignmentDummy { QArrayData header; T data; };
|
class AlignmentDummy { QArrayData header; T data; };
|
||||||
|
|
||||||
static QTypedArrayData *allocate(size_t capacity, bool reserve = false,
|
static QTypedArrayData *allocate(size_t capacity,
|
||||||
bool sharable = true) Q_REQUIRED_RESULT
|
AllocateOptions options = Default) Q_REQUIRED_RESULT
|
||||||
{
|
{
|
||||||
return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T),
|
return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T),
|
||||||
Q_ALIGNOF(AlignmentDummy), capacity, reserve, sharable));
|
Q_ALIGNOF(AlignmentDummy), capacity, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deallocate(QArrayData *data)
|
static void deallocate(QArrayData *data)
|
||||||
|
@ -66,7 +66,7 @@ public:
|
|||||||
QArrayDataPointer(const QArrayDataPointer &other)
|
QArrayDataPointer(const QArrayDataPointer &other)
|
||||||
: d(other.d->ref.ref()
|
: d(other.d->ref.ref()
|
||||||
? other.d
|
? other.d
|
||||||
: other.clone())
|
: other.clone(other.d->cloneFlags()))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +115,9 @@ public:
|
|||||||
void setSharable(bool sharable)
|
void setSharable(bool sharable)
|
||||||
{
|
{
|
||||||
if (d->alloc == 0 && d->size == 0) {
|
if (d->alloc == 0 && d->size == 0) {
|
||||||
d = Data::allocate(0, false, sharable);
|
d = Data::allocate(0, sharable
|
||||||
|
? QArrayData::Default
|
||||||
|
: QArrayData::Unsharable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +139,7 @@ public:
|
|||||||
bool detach()
|
bool detach()
|
||||||
{
|
{
|
||||||
if (d->ref.isShared()) {
|
if (d->ref.isShared()) {
|
||||||
Data *copy = clone();
|
Data *copy = clone(d->detachFlags());
|
||||||
QArrayDataPointer old(d);
|
QArrayDataPointer old(d);
|
||||||
d = copy;
|
d = copy;
|
||||||
return true;
|
return true;
|
||||||
@ -147,10 +149,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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,
|
QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size,
|
||||||
d->capacityReserved));
|
options));
|
||||||
if (d->size)
|
if (d->size)
|
||||||
copy->copyAppend(d->begin(), d->end());
|
copy->copyAppend(d->begin(), d->end());
|
||||||
|
|
||||||
|
@ -140,7 +140,8 @@ public:
|
|||||||
|| (n
|
|| (n
|
||||||
&& !d->capacityReserved
|
&& !d->capacityReserved
|
||||||
&& (d->ref != 1 || (d->capacityReserved = 1, false)))) {
|
&& (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.d->copyAppend(constBegin(), constEnd());
|
||||||
detached.swap(*this);
|
detached.swap(*this);
|
||||||
}
|
}
|
||||||
@ -160,7 +161,8 @@ public:
|
|||||||
if (d->ref != 1
|
if (d->ref != 1
|
||||||
|| capacity() - size() < size_t(last - first)) {
|
|| capacity() - size() < size_t(last - first)) {
|
||||||
SimpleVector detached(Data::allocate(
|
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(first, last);
|
||||||
detached.d->copyAppend(begin, begin + d->size);
|
detached.d->copyAppend(begin, begin + d->size);
|
||||||
@ -180,7 +182,8 @@ public:
|
|||||||
if (d->ref != 1
|
if (d->ref != 1
|
||||||
|| capacity() - size() < size_t(last - first)) {
|
|| capacity() - size() < size_t(last - first)) {
|
||||||
SimpleVector detached(Data::allocate(
|
SimpleVector detached(Data::allocate(
|
||||||
qMax(capacity(), size() + (last - first)), d->capacityReserved));
|
qMax(capacity(), size() + (last - first)),
|
||||||
|
d->detachFlags()));
|
||||||
|
|
||||||
if (d->size) {
|
if (d->size) {
|
||||||
const T *const begin = constBegin();
|
const T *const begin = constBegin();
|
||||||
@ -219,7 +222,8 @@ public:
|
|||||||
if (d->ref != 1
|
if (d->ref != 1
|
||||||
|| capacity() - size() < size_t(last - first)) {
|
|| capacity() - size() < size_t(last - first)) {
|
||||||
SimpleVector detached(Data::allocate(
|
SimpleVector detached(Data::allocate(
|
||||||
qMax(capacity(), size() + (last - first)), d->capacityReserved));
|
qMax(capacity(), size() + (last - first)),
|
||||||
|
d->detachFlags()));
|
||||||
|
|
||||||
if (position)
|
if (position)
|
||||||
detached.d->copyAppend(begin, where);
|
detached.d->copyAppend(begin, where);
|
||||||
|
@ -153,7 +153,7 @@ void tst_QArrayData::referenceCounting()
|
|||||||
void tst_QArrayData::sharedNullEmpty()
|
void tst_QArrayData::sharedNullEmpty()
|
||||||
{
|
{
|
||||||
QArrayData *null = const_cast<QArrayData *>(&QArrayData::shared_null);
|
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.isStatic());
|
||||||
QVERIFY(null->ref.isSharable());
|
QVERIFY(null->ref.isSharable());
|
||||||
@ -473,11 +473,13 @@ struct Deallocator
|
|||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(const QArrayData *)
|
Q_DECLARE_METATYPE(const QArrayData *)
|
||||||
|
Q_DECLARE_METATYPE(QArrayData::AllocateOptions)
|
||||||
|
|
||||||
void tst_QArrayData::allocate_data()
|
void tst_QArrayData::allocate_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<size_t>("objectSize");
|
QTest::addColumn<size_t>("objectSize");
|
||||||
QTest::addColumn<size_t>("alignment");
|
QTest::addColumn<size_t>("alignment");
|
||||||
|
QTest::addColumn<QArrayData::AllocateOptions>("allocateOptions");
|
||||||
QTest::addColumn<bool>("isCapacityReserved");
|
QTest::addColumn<bool>("isCapacityReserved");
|
||||||
QTest::addColumn<bool>("isSharable");
|
QTest::addColumn<bool>("isSharable");
|
||||||
QTest::addColumn<const QArrayData *>("commonEmpty");
|
QTest::addColumn<const QArrayData *>("commonEmpty");
|
||||||
@ -492,22 +494,25 @@ void tst_QArrayData::allocate_data()
|
|||||||
{ "void *", sizeof(void *), Q_ALIGNOF(void *) }
|
{ "void *", sizeof(void *), Q_ALIGNOF(void *) }
|
||||||
};
|
};
|
||||||
|
|
||||||
QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, true);
|
QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0);
|
||||||
QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, false, false);
|
QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable);
|
||||||
|
|
||||||
QVERIFY(shared_empty);
|
QVERIFY(shared_empty);
|
||||||
QVERIFY(unsharable_empty);
|
QVERIFY(unsharable_empty);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
char const *description;
|
char const *description;
|
||||||
|
QArrayData::AllocateOptions allocateOptions;
|
||||||
bool isCapacityReserved;
|
bool isCapacityReserved;
|
||||||
bool isSharable;
|
bool isSharable;
|
||||||
const QArrayData *commonEmpty;
|
const QArrayData *commonEmpty;
|
||||||
} options[] = {
|
} options[] = {
|
||||||
{ "Default", false, true, shared_empty },
|
{ "Default", QArrayData::Default, false, true, shared_empty },
|
||||||
{ "Reserved", true, true, shared_empty },
|
{ "Reserved", QArrayData::CapacityReserved, true, true, shared_empty },
|
||||||
{ "Reserved | Unsharable", true, false, unsharable_empty },
|
{ "Reserved | Unsharable",
|
||||||
{ "Unsharable", false, false, unsharable_empty },
|
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)
|
for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
|
||||||
@ -517,14 +522,15 @@ void tst_QArrayData::allocate_data()
|
|||||||
+ QLatin1String(": ")
|
+ QLatin1String(": ")
|
||||||
+ QLatin1String(options[j].description)))
|
+ QLatin1String(options[j].description)))
|
||||||
<< types[i].objectSize << types[i].alignment
|
<< types[i].objectSize << types[i].alignment
|
||||||
<< options[j].isCapacityReserved << options[j].isSharable
|
<< options[j].allocateOptions << options[j].isCapacityReserved
|
||||||
<< options[j].commonEmpty;
|
<< options[j].isSharable << options[j].commonEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QArrayData::allocate()
|
void tst_QArrayData::allocate()
|
||||||
{
|
{
|
||||||
QFETCH(size_t, objectSize);
|
QFETCH(size_t, objectSize);
|
||||||
QFETCH(size_t, alignment);
|
QFETCH(size_t, alignment);
|
||||||
|
QFETCH(QArrayData::AllocateOptions, allocateOptions);
|
||||||
QFETCH(bool, isCapacityReserved);
|
QFETCH(bool, isCapacityReserved);
|
||||||
QFETCH(bool, isSharable);
|
QFETCH(bool, isSharable);
|
||||||
QFETCH(const QArrayData *, commonEmpty);
|
QFETCH(const QArrayData *, commonEmpty);
|
||||||
@ -535,14 +541,14 @@ void tst_QArrayData::allocate()
|
|||||||
|
|
||||||
// Shared Empty
|
// Shared Empty
|
||||||
QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0,
|
QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0,
|
||||||
isCapacityReserved, isSharable), commonEmpty);
|
QArrayData::AllocateOptions(allocateOptions)), commonEmpty);
|
||||||
|
|
||||||
Deallocator keeper(objectSize, minAlignment);
|
Deallocator keeper(objectSize, minAlignment);
|
||||||
keeper.headers.reserve(1024);
|
keeper.headers.reserve(1024);
|
||||||
|
|
||||||
for (int capacity = 1; capacity <= 1024; capacity <<= 1) {
|
for (int capacity = 1; capacity <= 1024; capacity <<= 1) {
|
||||||
QArrayData *data = QArrayData::allocate(objectSize, minAlignment,
|
QArrayData *data = QArrayData::allocate(objectSize, minAlignment,
|
||||||
capacity, isCapacityReserved, isSharable);
|
capacity, QArrayData::AllocateOptions(allocateOptions));
|
||||||
keeper.headers.append(data);
|
keeper.headers.append(data);
|
||||||
|
|
||||||
QCOMPARE(data->size, 0);
|
QCOMPARE(data->size, 0);
|
||||||
@ -584,7 +590,7 @@ void tst_QArrayData::alignment()
|
|||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
QArrayData *data = QArrayData::allocate(sizeof(Unaligned),
|
QArrayData *data = QArrayData::allocate(sizeof(Unaligned),
|
||||||
minAlignment, 8, false, true);
|
minAlignment, 8, QArrayData::Default);
|
||||||
keeper.headers.append(data);
|
keeper.headers.append(data);
|
||||||
|
|
||||||
QVERIFY(data);
|
QVERIFY(data);
|
||||||
@ -952,10 +958,14 @@ void tst_QArrayData::setSharable_data()
|
|||||||
{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
|
{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5, true, true));
|
QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5,
|
||||||
QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10, false, true));
|
QArrayData::CapacityReserved));
|
||||||
QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15, true, true));
|
QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10,
|
||||||
QArrayDataPointer<int> staticArray(static_cast<QTypedArrayData<int> *>(&staticArrayData.header));
|
QArrayData::Default));
|
||||||
|
QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15,
|
||||||
|
QArrayData::CapacityReserved));
|
||||||
|
QArrayDataPointer<int> staticArray(
|
||||||
|
static_cast<QTypedArrayData<int> *>(&staticArrayData.header));
|
||||||
|
|
||||||
nonEmpty->copyAppend(5, 1);
|
nonEmpty->copyAppend(5, 1);
|
||||||
nonEmptyReserved->copyAppend(7, 2);
|
nonEmptyReserved->copyAppend(7, 2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user