diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 5ed8d4ba95..d5d96efc08 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -43,6 +43,7 @@ #define QARRAYDATA_H #include +#include QT_BEGIN_HEADER @@ -190,6 +191,84 @@ struct QArrayDataPointerRef & ~(Q_ALIGNOF(type) - 1) } \ /**/ +//////////////////////////////////////////////////////////////////////////////// +// Q_ARRAY_LITERAL + +// The idea here is to place a (read-only) copy of header and array data in an +// mmappable portion of the executable (typically, .rodata section). This is +// accomplished by hiding a static const instance of QStaticArrayData, which is +// POD. + +#if defined(Q_COMPILER_VARIADIC_MACROS) +#if defined(Q_COMPILER_LAMBDA) +// Hide array inside a lambda +#define Q_ARRAY_LITERAL(Type, ...) \ + ([]() -> QArrayDataPointerRef { \ + /* MSVC 2010 Doesn't support static variables in a lambda, but */ \ + /* happily accepts them in a static function of a lambda-local */ \ + /* struct :-) */ \ + struct StaticWrapper { \ + static QArrayDataPointerRef get() \ + { \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + return ref; \ + } \ + }; \ + return StaticWrapper::get(); \ + }()) \ + /**/ +#elif defined(Q_CC_GNU) +// Hide array within GCC's __extension__ {( )} block +#define Q_ARRAY_LITERAL(Type, ...) \ + __extension__ ({ \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + ref; \ + }) \ + /**/ +#endif +#endif // defined(Q_COMPILER_VARIADIC_MACROS) + +#if defined(Q_ARRAY_LITERAL) +#define Q_ARRAY_LITERAL_IMPL(Type, ...) \ + union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \ + \ + /* Portable compile-time array size computation */ \ + Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \ + enum { Size = sizeof(data) / sizeof(data[0]) }; \ + \ + static const QStaticArrayData literal = { \ + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(Type, Size), { __VA_ARGS__ } }; \ + \ + QArrayDataPointerRef ref = \ + { static_cast *>( \ + const_cast(&literal.header)) }; \ + /**/ +#else +// As a fallback, memory is allocated and data copied to the heap. + +// The fallback macro does NOT use variadic macros and does NOT support +// variable number of arguments. It is suitable for char arrays. + +namespace QtPrivate { + template + inline QArrayDataPointerRef qMakeArrayLiteral(const T (&array)[N]) + { + union { T type_must_be_POD; } dummy; Q_UNUSED(dummy) + + QArrayDataPointerRef result = { QTypedArrayData::allocate(N) }; + Q_CHECK_PTR(result.ptr); + + ::memcpy(result.ptr->data(), array, N * sizeof(T)); + result.ptr->size = N; + + return result; + } +} + +#define Q_ARRAY_LITERAL(Type, Array) \ + QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)( Array ) +#endif // !defined(Q_ARRAY_LITERAL) + QT_END_NAMESPACE QT_END_HEADER diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index d53b90d8a4..f210411643 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -77,6 +77,11 @@ public: d->copyAppend(begin, end); } + SimpleVector(QArrayDataPointerRef ptr) + : d(ptr) + { + } + explicit SimpleVector(Data *ptr) : d(ptr) { diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index d1da18d4e1..55aa2e5e75 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -82,6 +82,8 @@ private slots: void setSharable_data(); void setSharable(); void fromRawData(); + void literals(); + void variadicLiterals(); }; template const T &const_(const T &t) { return t; } @@ -1182,5 +1184,95 @@ void tst_QArrayData::fromRawData() } } +void tst_QArrayData::literals() +{ + { + QArrayDataPointer d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + QCOMPARE(d->size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], char('A' + i)); + } + + { + // wchar_t is not necessarily 2-bytes + QArrayDataPointer d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); + QCOMPARE(d->size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], wchar_t('A' + i)); + } + + { + SimpleVector v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(11)); + // v.capacity() is unspecified, for now + +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + QVERIFY(v.isStatic()); +#endif + + QVERIFY(v.isSharable()); + QVERIFY(v.constBegin() + v.size() == v.constEnd()); + + for (int i = 0; i < 10; ++i) + QCOMPARE(const_(v)[i], char('A' + i)); + QCOMPARE(const_(v)[10], char('\0')); + } +} + +void tst_QArrayData::variadicLiterals() +{ +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + { + QArrayDataPointer d = + Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], i); + } + + { + QArrayDataPointer d = Q_ARRAY_LITERAL(char, + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], char('A' + i)); + } + + { + QArrayDataPointer d = Q_ARRAY_LITERAL(const char *, + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) { + QCOMPARE(d->data()[i][0], char('A' + i)); + QCOMPARE(d->data()[i][1], '\0'); + } + } + + { + SimpleVector v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(7)); + // v.capacity() is unspecified, for now + + QVERIFY(v.isStatic()); + + QVERIFY(v.isSharable()); + QVERIFY(v.constBegin() + v.size() == v.constEnd()); + + for (int i = 0; i < 7; ++i) + QCOMPARE(const_(v)[i], i); + } +#else + QSKIP("Variadic Q_ARRAY_LITERAL not available in current configuration."); +#endif // defined(Q_COMPILER_VARIADIC_MACROS) +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc"