Enable QArrayData to reference external array data

By default, QTypedArrayData::fromRawData provides the same semantics as
already exist in QByteArray and QString (immutable, sharable data), but
more combinations are possible. In particular, immutable-unsharable
leaves the data owner in control of its lifetime by forcing deep copies.

As part of this, a new isMutable property is introduced in QArrayData.
This could be taken to be implicit in statics that are initialized with
a proper size but with alloc set to 0. QStringLiteral and QByteLiteral
already did this, forcing re-allocations on resize even before the
(static, thus shared) ref-count is considered.

The isMutable property detaches data mutability and shared status, which
are orthogonal concepts (at least in the unshared state). For the time
being, there is no API to explicitly (re)set mutability, but statics and
RawData mark data immutable.

Change-Id: I33a995a35e1c3d7a12391b1d7c36095aa28e221a
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
João Abecasis 2012-01-11 17:16:04 +01:00 committed by Qt by Nokia
parent 5b250d497f
commit f4c1e2c40f
5 changed files with 89 additions and 4 deletions

View File

@ -56,16 +56,19 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
&& !(alignment & (alignment - 1)));
// Don't allocate empty headers
if (!capacity)
if (!(options & RawData) && !capacity)
return !(options & Unsharable)
? const_cast<QArrayData *>(&qt_array_empty)
: const_cast<QArrayData *>(&qt_array_unsharable_empty);
size_t allocSize = sizeof(QArrayData) + objectSize * capacity;
// 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));
// Padding is skipped when allocating a header for RawData.
if (!(options & RawData))
allocSize += (alignment - Q_ALIGNOF(QArrayData));
QArrayData *header = static_cast<QArrayData *>(qMalloc(allocSize));
Q_CHECK_PTR(header);

View File

@ -73,9 +73,18 @@ struct Q_CORE_EXPORT QArrayData
return reinterpret_cast<const char *>(this) + offset;
}
// This refers to array data mutability, not "header data" represented by
// data members in QArrayData. Shared data (array and header) must still
// follow COW principles.
bool isMutable() const
{
return alloc != 0;
}
enum AllocateOption {
CapacityReserved = 0x1,
Unsharable = 0x2,
RawData = 0x4,
Default = 0
};
@ -139,6 +148,20 @@ struct QTypedArrayData
QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy));
}
static QTypedArrayData *fromRawData(const T *data, size_t n,
AllocateOptions options = Default)
{
QTypedArrayData *result = allocate(0, options | RawData);
if (result) {
Q_ASSERT(!result->ref.isShared()); // No shared empty, please!
result->offset = reinterpret_cast<const char *>(data)
- reinterpret_cast<const char *>(result);
result->size = n;
}
return result;
}
static QTypedArrayData *sharedNull()
{
return static_cast<QTypedArrayData *>(

View File

@ -114,6 +114,8 @@ public:
void setSharable(bool sharable)
{
// Can't call setSharable on static read-only data, like shared_null
// and the internal shared-empties.
if (d->alloc == 0 && d->size == 0) {
d = Data::allocate(0, sharable
? QArrayData::Default
@ -138,7 +140,7 @@ public:
bool detach()
{
if (d->ref.isShared()) {
if (!d->isMutable() || d->ref.isShared()) {
Data *copy = clone(d->detachFlags());
QArrayDataPointer old(d);
d = copy;

View File

@ -274,6 +274,12 @@ public:
d.detach();
}
static SimpleVector fromRawData(const T *data, size_t size,
QArrayData::AllocateOptions options = Data::Default)
{
return SimpleVector(Data::fromRawData(data, size, options));
}
private:
QArrayDataPointer<T> d;
};

View File

@ -81,6 +81,7 @@ private slots:
void arrayOps();
void setSharable_data();
void setSharable();
void fromRawData();
};
template <class T> const T &const_(const T &t) { return t; }
@ -1131,5 +1132,55 @@ void tst_QArrayData::setSharable()
QVERIFY(array->ref.isSharable());
}
void tst_QArrayData::fromRawData()
{
static const int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
{
// Default: Immutable, sharable
SimpleVector<int> raw = SimpleVector<int>::fromRawData(array,
sizeof(array)/sizeof(array[0]), QArrayData::Default);
QCOMPARE(raw.size(), size_t(11));
QCOMPARE(raw.constBegin(), array);
QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0]));
QVERIFY(!raw.isShared());
QVERIFY(SimpleVector<int>(raw).isSharedWith(raw));
QVERIFY(!raw.isShared());
// Detach
QCOMPARE(raw.back(), 11);
QVERIFY(raw.constBegin() != array);
}
{
// Immutable, unsharable
SimpleVector<int> raw = SimpleVector<int>::fromRawData(array,
sizeof(array)/sizeof(array[0]), QArrayData::Unsharable);
QCOMPARE(raw.size(), size_t(11));
QCOMPARE(raw.constBegin(), array);
QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0]));
SimpleVector<int> copy(raw);
QVERIFY(!copy.isSharedWith(raw));
QVERIFY(!raw.isShared());
QCOMPARE(copy.size(), size_t(11));
for (size_t i = 0; i < 11; ++i)
QCOMPARE(const_(copy)[i], const_(raw)[i]);
QCOMPARE(raw.size(), size_t(11));
QCOMPARE(raw.constBegin(), array);
QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0]));
// Detach
QCOMPARE(raw.back(), 11);
QVERIFY(raw.constBegin() != array);
}
}
QTEST_APPLESS_MAIN(tst_QArrayData)
#include "tst_qarraydata.moc"