Get rid of shared null for QByteArray, QString and QVector

As a side effect, data() can now return a nullptr. This
has the potential to cause crashes in existig code. To work
around this, return an empty string from QString::data()
and QByteArray::data() for now.

For Qt 6 (and once all our internal issues are fixed), data()
will by default return a nullptr for a null QString, but we'll
offer a #define to enable backwards compatible behavior.

Change-Id: I4f66d97ff1dce3eb99a239f1eab9106fa9b1741a
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Lars Knoll 2019-11-16 23:25:26 +01:00
parent 2e51686746
commit 76004502ba
18 changed files with 201 additions and 214 deletions

View File

@ -156,7 +156,8 @@ QString ProString::toQString() const
QString &ProString::toQString(QString &tmp) const QString &ProString::toQString(QString &tmp) const
{ {
return tmp.setRawData(m_string.constData() + m_offset, m_length); tmp = m_string.mid(m_offset, m_length);
return tmp;
} }
/* /*

View File

@ -270,7 +270,6 @@ class ProStringRoUser
public: public:
ProStringRoUser(QString &rs) ProStringRoUser(QString &rs)
{ {
Q_ASSERT(rs.isDetached() || rs.isEmpty());
m_rs = &rs; m_rs = &rs;
} }
ProStringRoUser(const ProString &ps, QString &rs) ProStringRoUser(const ProString &ps, QString &rs)

View File

@ -67,6 +67,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
const char QByteArray::_empty = '\0';
// ASCII case system, used by QByteArray::to{Upper,Lower}() and qstr(n)icmp(): // ASCII case system, used by QByteArray::to{Upper,Lower}() and qstr(n)icmp():
static constexpr inline uchar asciiUpper(uchar c) static constexpr inline uchar asciiUpper(uchar c)
{ {
@ -364,11 +366,18 @@ int qstrnicmp(const char *str1, const char *str2, uint len)
*/ */
int qstrnicmp(const char *str1, qsizetype len1, const char *str2, qsizetype len2) int qstrnicmp(const char *str1, qsizetype len1, const char *str2, qsizetype len2)
{ {
Q_ASSERT(str1);
Q_ASSERT(len1 >= 0); Q_ASSERT(len1 >= 0);
Q_ASSERT(len2 >= -1); Q_ASSERT(len2 >= -1);
const uchar *s1 = reinterpret_cast<const uchar *>(str1); const uchar *s1 = reinterpret_cast<const uchar *>(str1);
const uchar *s2 = reinterpret_cast<const uchar *>(str2); const uchar *s2 = reinterpret_cast<const uchar *>(str2);
if (!s1 || !len1) {
if (len2 == 0)
return 0;
if (len2 == -1)
return (!s2 || !*s2) ? 0 : -1;
Q_ASSERT(s2);
return -1;
}
if (!s2) if (!s2)
return len1 == 0 ? 0 : 1; return len1 == 0 ? 0 : 1;
@ -1694,7 +1703,7 @@ void QByteArray::expand(int i)
QByteArray QByteArray::nulTerminated() const QByteArray QByteArray::nulTerminated() const
{ {
// is this fromRawData? // is this fromRawData?
if (!IS_RAW_DATA(d)) if (d.isMutable())
return *this; // no, then we're sure we're zero terminated return *this; // no, then we're sure we're zero terminated
QByteArray copy(*this); QByteArray copy(*this);
@ -1725,7 +1734,7 @@ QByteArray QByteArray::nulTerminated() const
QByteArray &QByteArray::prepend(const QByteArray &ba) QByteArray &QByteArray::prepend(const QByteArray &ba)
{ {
if (size() == 0 && d->isStatic() && !IS_RAW_DATA(ba.d)) { if (size() == 0 && d->isStatic() && ba.d.isMutable()) {
*this = ba; *this = ba;
} else if (ba.size() != 0) { } else if (ba.size() != 0) {
QByteArray tmp = *this; QByteArray tmp = *this;
@ -1818,7 +1827,7 @@ QByteArray &QByteArray::prepend(char ch)
QByteArray &QByteArray::append(const QByteArray &ba) QByteArray &QByteArray::append(const QByteArray &ba)
{ {
if (size() == 0 && d->isStatic() && !IS_RAW_DATA(ba.d)) { if (size() == 0 && d->isStatic() && ba.d.isMutable()) {
*this = ba; *this = ba;
} else if (ba.size() != 0) { } else if (ba.size() != 0) {
if (d->needsDetach() || size() + ba.size() > capacity()) if (d->needsDetach() || size() + ba.size() > capacity())
@ -2507,6 +2516,8 @@ static qsizetype lastIndexOfHelper(const char *haystack, qsizetype l, const char
int QByteArray::lastIndexOf(const QByteArray &ba, int from) const int QByteArray::lastIndexOf(const QByteArray &ba, int from) const
{ {
if (isEmpty())
return !ba.size() ? 0 : -1;
const int ol = ba.size(); const int ol = ba.size();
if (ol == 1) if (ol == 1)
return lastIndexOf(ba[0], from); return lastIndexOf(ba[0], from);
@ -2524,6 +2535,8 @@ int QByteArray::lastIndexOf(const QByteArray &ba, int from) const
*/ */
int QByteArray::lastIndexOf(const char *str, int from) const int QByteArray::lastIndexOf(const char *str, int from) const
{ {
if (isEmpty())
return (str && *str) ? -1 : 0;
const int ol = qstrlen(str); const int ol = qstrlen(str);
if (ol == 1) if (ol == 1)
return lastIndexOf(*str, from); return lastIndexOf(*str, from);
@ -3484,6 +3497,11 @@ T toIntegral_helper(const char *data, bool *ok, int base)
base = 10; base = 10;
} }
#endif #endif
if (!data) {
if (ok)
*ok = false;
return 0;
}
// we select the right overload by the last, unused parameter // we select the right overload by the last, unused parameter
Int64 val = toIntegral_helper(data, ok, base, Int64()); Int64 val = toIntegral_helper(data, ok, base, Int64());
@ -4154,6 +4172,8 @@ QByteArray QByteArray::number(double n, char f, int prec)
} }
/*! /*!
\fn QByteArray QByteArray::fromRawData(const char *data, int size) constexpr
Constructs a QByteArray that uses the first \a size bytes of the Constructs a QByteArray that uses the first \a size bytes of the
\a data array. The bytes are \e not copied. The QByteArray will \a data array. The bytes are \e not copied. The QByteArray will
contain the \a data pointer. The caller guarantees that \a data contain the \a data pointer. The caller guarantees that \a data
@ -4187,18 +4207,6 @@ QByteArray QByteArray::number(double n, char f, int prec)
\sa setRawData(), data(), constData() \sa setRawData(), data(), constData()
*/ */
QByteArray QByteArray::fromRawData(const char *data, int size)
{
QByteArray::DataPointer x;
if (!data) {
} else if (!size) {
x = DataPointer(Data::allocate(0), 0);
} else {
x = Data::fromRawData(data, size);
}
return QByteArray(x);
}
/*! /*!
\since 4.7 \since 4.7

View File

@ -54,6 +54,11 @@
#include <string> #include <string>
#include <iterator> #include <iterator>
#ifndef QT5_NULL_STRINGS
// ### Should default to 0 in Qt 6.0
#define QT5_NULL_STRINGS 1
#endif
#ifdef truncate #ifdef truncate
#error qbytearray.h must be included before any header file that defines truncate #error qbytearray.h must be included before any header file that defines truncate
#endif #endif
@ -117,16 +122,7 @@ class QDataStream;
using QByteArrayData = QArrayDataPointer<char>; using QByteArrayData = QArrayDataPointer<char>;
# define QByteArrayLiteral(str) \ # define QByteArrayLiteral(str) \
([]() -> QByteArray { \ (QByteArray(QByteArrayData(nullptr, const_cast<char *>(str), sizeof(str) - 1))) \
enum { Size = sizeof(str) - 1 }; \
static const QArrayData qbytearray_literal = { \
Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }; \
QByteArrayData holder = { \
static_cast<QTypedArrayData<char> *>(const_cast<QArrayData *>(&qbytearray_literal)), \
const_cast<char *>(str), \
Size }; \
return QByteArray(holder); \
}()) \
/**/ /**/
class Q_CORE_EXPORT QByteArray class Q_CORE_EXPORT QByteArray
@ -137,6 +133,7 @@ private:
typedef QTypedArrayData<char> Data; typedef QTypedArrayData<char> Data;
DataPointer d; DataPointer d;
static const char _empty;
public: public:
enum Base64Option { enum Base64Option {
@ -158,7 +155,7 @@ public:
IllegalPadding, IllegalPadding,
}; };
inline QByteArray() noexcept; inline constexpr QByteArray() noexcept;
QByteArray(const char *, int size = -1); QByteArray(const char *, int size = -1);
QByteArray(int size, char c); QByteArray(int size, char c);
QByteArray(int size, Qt::Initialization); QByteArray(int size, Qt::Initialization);
@ -357,7 +354,10 @@ public:
Q_REQUIRED_RESULT static QByteArray number(qlonglong, int base = 10); Q_REQUIRED_RESULT static QByteArray number(qlonglong, int base = 10);
Q_REQUIRED_RESULT static QByteArray number(qulonglong, int base = 10); Q_REQUIRED_RESULT static QByteArray number(qulonglong, int base = 10);
Q_REQUIRED_RESULT static QByteArray number(double, char f = 'g', int prec = 6); Q_REQUIRED_RESULT static QByteArray number(double, char f = 'g', int prec = 6);
Q_REQUIRED_RESULT static QByteArray fromRawData(const char *, int size); Q_REQUIRED_RESULT static QByteArray fromRawData(const char *data, int size)
{
return QByteArray(DataPointer(nullptr, const_cast<char *>(data), size));
}
class FromBase64Result; class FromBase64Result;
Q_REQUIRED_RESULT static FromBase64Result fromBase64Encoding(QByteArray &&base64, Base64Options options = Base64Encoding); Q_REQUIRED_RESULT static FromBase64Result fromBase64Encoding(QByteArray &&base64, Base64Options options = Base64Encoding);
@ -449,7 +449,7 @@ private:
Q_DECLARE_OPERATORS_FOR_FLAGS(QByteArray::Base64Options) Q_DECLARE_OPERATORS_FOR_FLAGS(QByteArray::Base64Options)
inline QByteArray::QByteArray() noexcept {} inline constexpr QByteArray::QByteArray() noexcept {}
inline QByteArray::~QByteArray() {} inline QByteArray::~QByteArray() {}
inline char QByteArray::at(int i) const inline char QByteArray::at(int i) const
@ -466,11 +466,21 @@ inline QByteArray::operator const void *() const
{ return data(); } { return data(); }
#endif #endif
inline char *QByteArray::data() inline char *QByteArray::data()
{ detach(); return d.data(); } {
detach();
Q_ASSERT(d.data());
return d.data();
}
inline const char *QByteArray::data() const inline const char *QByteArray::data() const
{ return d.data(); } {
#if QT5_NULL_STRINGS == 1
return d.data() ? d.data() : &_empty;
#else
return d.data();
#endif
}
inline const char *QByteArray::constData() const inline const char *QByteArray::constData() const
{ return d.data(); } { return data(); }
inline void QByteArray::detach() inline void QByteArray::detach()
{ if (d->needsDetach()) reallocData(uint(size()) + 1u, d->detachFlags()); } { if (d->needsDetach()) reallocData(uint(size()) + 1u, d->detachFlags()); }
inline bool QByteArray::isDetached() const inline bool QByteArray::isDetached() const
@ -486,7 +496,7 @@ inline void QByteArray::reserve(int asize)
if (d->needsDetach() || asize > capacity()) { if (d->needsDetach() || asize > capacity()) {
reallocData(qMax(uint(size()), uint(asize)) + 1u, d->detachFlags() | Data::CapacityReserved); reallocData(qMax(uint(size()), uint(asize)) + 1u, d->detachFlags() | Data::CapacityReserved);
} else { } else {
d->flags() |= Data::CapacityReserved; d->setFlag(Data::CapacityReserved);
} }
} }
@ -497,7 +507,7 @@ inline void QByteArray::squeeze()
if (d->needsDetach() || size() < capacity()) { if (d->needsDetach() || size() < capacity()) {
reallocData(uint(size()) + 1u, d->detachFlags() & ~Data::CapacityReserved); reallocData(uint(size()) + 1u, d->detachFlags() & ~Data::CapacityReserved);
} else { } else {
d->flags() &= uint(~Data::CapacityReserved); d->clearFlag(Data::CapacityReserved);
} }
} }

View File

@ -106,6 +106,8 @@ static constexpr bool points_into_range(const T *p, const T *b, const T *e, Cmp
return !less(p, b) && less(p, e); return !less(p, b) && less(p, e);
} }
const char16_t QString::_empty = 0;
/* /*
* Note on the use of SIMD in qstring.cpp: * Note on the use of SIMD in qstring.cpp:
* *
@ -4549,7 +4551,7 @@ QString QString::mid(int position, int n) const
{ {
QPair<Data *, char16_t *> pair = Data::allocate(0); QPair<Data *, char16_t *> pair = Data::allocate(0);
DataPointer empty = { pair.first, pair.second, 0 }; DataPointer empty = { pair.first, pair.second, 0 };
return QString(empty); return QString(std::move(empty));
} }
case QContainerImplHelper::Full: case QContainerImplHelper::Full:
return *this; return *this;
@ -5066,7 +5068,7 @@ QString QString::fromLocal8Bit_helper(const char *str, int size)
if (size == 0 || (!*str && size < 0)) { if (size == 0 || (!*str && size < 0)) {
QPair<Data *, char16_t *> pair = Data::allocate(0); QPair<Data *, char16_t *> pair = Data::allocate(0);
QString::DataPointer empty = { pair.first, pair.second, 0 }; QString::DataPointer empty = { pair.first, pair.second, 0 };
return QString(empty); return QString(std::move(empty));
} }
QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless); QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
return toUtf16(str, size); return toUtf16(str, size);
@ -8617,14 +8619,7 @@ bool QString::isRightToLeft() const
*/ */
QString QString::fromRawData(const QChar *unicode, int size) QString QString::fromRawData(const QChar *unicode, int size)
{ {
QString::DataPointer x; return QString(Data::fromRawData(const_cast<char16_t *>(reinterpret_cast<const char16_t *>(unicode)), size));
if (!unicode) {
} else if (!size) {
x = DataPointer(Data::allocate(0), 0);
} else {
x = Data::fromRawData(reinterpret_cast<const char16_t *>(unicode), size);
}
return QString(x);
} }
/*! /*!

View File

@ -275,7 +275,7 @@ class Q_CORE_EXPORT QString
public: public:
typedef QStringPrivate DataPointer; typedef QStringPrivate DataPointer;
inline QString() noexcept; inline constexpr QString() noexcept;
explicit QString(const QChar *unicode, int size = -1); explicit QString(const QChar *unicode, int size = -1);
QString(QChar c); QString(QChar c);
QString(int size, QChar c); QString(int size, QChar c);
@ -902,7 +902,7 @@ public:
{ return QStringView(*this).isValidUtf16(); } { return QStringView(*this).isValidUtf16(); }
QString(int size, Qt::Initialization); QString(int size, Qt::Initialization);
explicit QString(DataPointer dd) : d(dd) {} explicit QString(DataPointer &&dd) : d(std::move(dd)) {}
private: private:
#if defined(QT_NO_CAST_FROM_ASCII) #if defined(QT_NO_CAST_FROM_ASCII)
@ -915,6 +915,7 @@ private:
#endif #endif
DataPointer d; DataPointer d;
static const char16_t _empty;
friend inline bool operator==(QChar, const QString &) noexcept; friend inline bool operator==(QChar, const QString &) noexcept;
friend inline bool operator< (QChar, const QString &) noexcept; friend inline bool operator< (QChar, const QString &) noexcept;
@ -1031,13 +1032,23 @@ inline const QChar QString::operator[](int i) const
inline bool QString::isEmpty() const inline bool QString::isEmpty() const
{ return d.size == 0; } { return d.size == 0; }
inline const QChar *QString::unicode() const inline const QChar *QString::unicode() const
{ return reinterpret_cast<const QChar*>(d.data()); } { return data(); }
inline const QChar *QString::data() const inline const QChar *QString::data() const
{ return reinterpret_cast<const QChar*>(d.data()); } {
#if QT5_NULL_STRINGS == 1
return reinterpret_cast<const QChar *>(d.data() ? d.data() : &_empty);
#else
return reinterpret_cast<const QChar *>(d.data());
#endif
}
inline QChar *QString::data() inline QChar *QString::data()
{ detach(); return reinterpret_cast<QChar*>(d.data()); } {
detach();
Q_ASSERT(d.data());
return reinterpret_cast<QChar *>(d.data());
}
inline const QChar *QString::constData() const inline const QChar *QString::constData() const
{ return reinterpret_cast<const QChar*>(d.data()); } { return data(); }
inline void QString::detach() inline void QString::detach()
{ if (d->needsDetach()) reallocData(d.size + 1u); } { if (d->needsDetach()) reallocData(d.size + 1u); }
inline bool QString::isDetached() const inline bool QString::isDetached() const
@ -1107,7 +1118,7 @@ inline QString QString::fromWCharArray(const wchar_t *string, int size)
: fromUcs4(reinterpret_cast<const char32_t *>(string), size); : fromUcs4(reinterpret_cast<const char32_t *>(string), size);
} }
inline QString::QString() noexcept {} inline constexpr QString::QString() noexcept {}
inline QString::~QString() {} inline QString::~QString() {}
inline void QString::reserve(int asize) inline void QString::reserve(int asize)
@ -1116,7 +1127,7 @@ inline void QString::reserve(int asize)
reallocData(uint(qMax(asize, size())) + 1u); reallocData(uint(qMax(asize, size())) + 1u);
// we're not shared anymore, for sure // we're not shared anymore, for sure
d->flags() |= Data::CapacityReserved; d->setFlag(Data::CapacityReserved);
} }
inline void QString::squeeze() inline void QString::squeeze()
@ -1127,7 +1138,7 @@ inline void QString::squeeze()
reallocData(uint(d.size) + 1u); reallocData(uint(d.size) + 1u);
// we're not shared anymore, for sure // we're not shared anymore, for sure
d->flags() &= uint(~Data::CapacityReserved); d->clearFlag(Data::CapacityReserved);
} }
inline QString &QString::setUtf16(const ushort *autf16, int asize) inline QString &QString::setUtf16(const ushort *autf16, int asize)

View File

@ -62,18 +62,9 @@ static_assert(sizeof(qunicodechar) == 2,
#define QT_UNICODE_LITERAL(str) u"" str #define QT_UNICODE_LITERAL(str) u"" str
#define QStringLiteral(str) \ #define QStringLiteral(str) \
([]() noexcept -> QString { \ (QString(QStringPrivate(nullptr, \
enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ reinterpret_cast<char16_t *>(const_cast<qunicodechar *>(QT_UNICODE_LITERAL(str))), \
static const QArrayData qstring_literal = { \ sizeof(QT_UNICODE_LITERAL(str))/2 - 1))) \
Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 \
}; \
QStringPrivate holder = { \
static_cast<QTypedArrayData<char16_t> *>(const_cast<QArrayData *>(&qstring_literal)), \
const_cast<qunicodechar *>(QT_UNICODE_LITERAL(str)), \
Size \
}; \
return QString(holder); \
}()) \
/**/ /**/
using QStringPrivate = QArrayDataPointer<char16_t>; using QStringPrivate = QArrayDataPointer<char16_t>;

View File

@ -232,25 +232,15 @@ void *QArrayData::allocate(QArrayData **dptr, size_t objectSize, size_t alignmen
return reinterpret_cast<void *>(data); return reinterpret_cast<void *>(data);
} }
QArrayData *QArrayData::prepareRawData(ArrayOptions options) Q_DECL_NOTHROW
{
QArrayData *header = allocateData(sizeof(QArrayData), (options & ~DataTypeBits) | RawDataType);
if (header)
header->alloc = 0;
return header;
}
QPair<QArrayData *, void *> QPair<QArrayData *, void *>
QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer, QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
size_t objectSize, size_t capacity, ArrayOptions options) noexcept size_t objectSize, size_t capacity, ArrayOptions options) noexcept
{ {
Q_ASSERT(data); Q_ASSERT(!data || !data->isShared());
Q_ASSERT(data->isMutable());
Q_ASSERT(!data->isShared());
size_t headerSize = sizeof(QArrayData); size_t headerSize = sizeof(QArrayData);
size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
qptrdiff offset = reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data); qptrdiff offset = dataPointer ? reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data) : headerSize;
options |= AllocatedDataType | MutableData; options |= AllocatedDataType | MutableData;
QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize))); QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize)));
if (header) { if (header) {

View File

@ -143,14 +143,6 @@ struct Q_CORE_EXPORT QArrayData
return result; return result;
} }
ArrayOptions cloneFlags() const
{
ArrayOptions result = DefaultAllocationFlags;
if (flags & CapacityReserved)
result |= CapacityReserved;
return result;
}
Q_REQUIRED_RESULT Q_REQUIRED_RESULT
#if defined(Q_CC_GNU) #if defined(Q_CC_GNU)
__attribute__((__malloc__)) __attribute__((__malloc__))
@ -159,8 +151,6 @@ struct Q_CORE_EXPORT QArrayData
size_t capacity, ArrayOptions options = DefaultAllocationFlags) noexcept; size_t capacity, ArrayOptions options = DefaultAllocationFlags) noexcept;
Q_REQUIRED_RESULT static QPair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer, Q_REQUIRED_RESULT static QPair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer,
size_t objectSize, size_t newCapacity, ArrayOptions newOptions = DefaultAllocationFlags) Q_DECL_NOTHROW; size_t objectSize, size_t newCapacity, ArrayOptions newOptions = DefaultAllocationFlags) Q_DECL_NOTHROW;
Q_REQUIRED_RESULT static QArrayData *prepareRawData(ArrayOptions options = ArrayOptions(RawDataType))
Q_DECL_NOTHROW;
static void deallocate(QArrayData *data, size_t objectSize, static void deallocate(QArrayData *data, size_t objectSize,
size_t alignment) noexcept; size_t alignment) noexcept;
@ -229,36 +219,14 @@ struct QTypedArrayData
QArrayData::deallocate(data, sizeof(T), alignof(AlignmentDummy)); QArrayData::deallocate(data, sizeof(T), alignof(AlignmentDummy));
} }
static QArrayDataPointerRef<T> fromRawData(const T *data, size_t n, static QArrayDataPointerRef<T> fromRawData(const T *data, size_t n)
ArrayOptions options = DefaultRawFlags)
{ {
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
QArrayDataPointerRef<T> result = { QArrayDataPointerRef<T> result = {
static_cast<QTypedArrayData *>(prepareRawData(options)), const_cast<T *>(data), uint(n) nullptr, const_cast<T *>(data), uint(n)
}; };
if (result.ptr) {
Q_ASSERT(!result.ptr->isShared()); // No shared empty, please!
}
return result; return result;
} }
static QTypedArrayData *sharedNull() noexcept
{
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
return static_cast<QTypedArrayData *>(QArrayData::sharedNull());
}
static QTypedArrayData *sharedEmpty()
{
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
return allocate(/* capacity */ 0);
}
static T *sharedNullData()
{
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
return static_cast<T *>(QArrayData::sharedNullData());
}
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -272,32 +240,10 @@ struct QTypedArrayData
// Hide array inside a lambda // Hide array inside a lambda
#define Q_ARRAY_LITERAL(Type, ...) \ #define Q_ARRAY_LITERAL(Type, ...) \
([]() -> QArrayDataPointerRef<Type> { \ ([]() -> QArrayDataPointerRef<Type> { \
/* MSVC 2010 Doesn't support static variables in a lambda, but */ \ static Type const data[] = { __VA_ARGS__ }; \
/* happily accepts them in a static function of a lambda-local */ \
/* struct :-) */ \
struct StaticWrapper { \
static QArrayDataPointerRef<Type> get() \
{ \
Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \
return ref; \
} \
}; \
return StaticWrapper::get(); \
}()) \
/**/
#define Q_ARRAY_LITERAL_IMPL(Type, ...) \
/* Portable compile-time array size computation */ \
static constexpr Type data[] = { __VA_ARGS__ }; \
enum { Size = sizeof(data) / sizeof(data[0]) }; \ enum { Size = sizeof(data) / sizeof(data[0]) }; \
\ return { nullptr, const_cast<Type *>(data), Size }; \
static constexpr QArrayData literal = { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 };\ }())
\
QArrayDataPointerRef<Type> ref = \
{ static_cast<QTypedArrayData<Type> *>( \
const_cast<QArrayData *>(&literal)), \
const_cast<Type *>(data), \
Size }; \
/**/ /**/
namespace QtPrivate { namespace QtPrivate {

View File

@ -134,7 +134,7 @@ public:
void destroyAll() // Call from destructors, ONLY! void destroyAll() // Call from destructors, ONLY!
{ {
Q_ASSERT(this->isMutable()); Q_ASSERT(this->d);
Q_ASSERT(this->d->ref_.loadRelaxed() == 0); Q_ASSERT(this->d->ref_.loadRelaxed() == 0);
// As this is to be called only from destructor, it doesn't need to be // As this is to be called only from destructor, it doesn't need to be
@ -334,7 +334,7 @@ struct QGenericArrayOps
void destroyAll() // Call from destructors, ONLY void destroyAll() // Call from destructors, ONLY
{ {
Q_ASSERT(this->isMutable()); Q_ASSERT(this->d);
// As this is to be called only from destructor, it doesn't need to be // As this is to be called only from destructor, it doesn't need to be
// exception safe; size not updated. // exception safe; size not updated.

View File

@ -59,18 +59,18 @@ public:
typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type; typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type;
QArrayDataPointer() noexcept constexpr QArrayDataPointer() noexcept
: d(Data::sharedNull()), ptr(Data::sharedNullData()), size(0) : d(nullptr), ptr(nullptr), size(0)
{ {
} }
QArrayDataPointer(const QArrayDataPointer &other) noexcept QArrayDataPointer(const QArrayDataPointer &other) noexcept
: d(other.d), ptr(other.ptr), size(other.size) : d(other.d), ptr(other.ptr), size(other.size)
{ {
other.d->ref(); ref();
} }
QArrayDataPointer(Data *header, T *adata, size_t n = 0) noexcept constexpr QArrayDataPointer(Data *header, T *adata, size_t n = 0) noexcept
: d(header), ptr(adata), size(int(n)) : d(header), ptr(adata), size(int(n))
{ {
} }
@ -96,46 +96,40 @@ public:
QArrayDataPointer(QArrayDataPointer &&other) noexcept QArrayDataPointer(QArrayDataPointer &&other) noexcept
: d(other.d), ptr(other.ptr), size(other.size) : d(other.d), ptr(other.ptr), size(other.size)
{ {
other.d = Data::sharedNull(); other.d = nullptr;
other.ptr = Data::sharedNullData(); other.ptr = nullptr;
other.size = 0; other.size = 0;
} }
QArrayDataPointer &operator=(QArrayDataPointer &&other) noexcept QArrayDataPointer &operator=(QArrayDataPointer &&other) noexcept
{ {
QArrayDataPointer moved(std::move(other)); this->swap(other);
this->swap(moved);
return *this; return *this;
} }
DataOps &operator*() noexcept DataOps &operator*() noexcept
{ {
Q_ASSERT(d);
return *static_cast<DataOps *>(this); return *static_cast<DataOps *>(this);
} }
DataOps *operator->() noexcept DataOps *operator->() noexcept
{ {
Q_ASSERT(d);
return static_cast<DataOps *>(this); return static_cast<DataOps *>(this);
} }
const DataOps &operator*() const noexcept const DataOps &operator*() const noexcept
{ {
Q_ASSERT(d);
return *static_cast<const DataOps *>(this); return *static_cast<const DataOps *>(this);
} }
const DataOps *operator->() const noexcept const DataOps *operator->() const noexcept
{ {
Q_ASSERT(d);
return static_cast<const DataOps *>(this); return static_cast<const DataOps *>(this);
} }
~QArrayDataPointer() ~QArrayDataPointer()
{ {
if (!deref()) { if (!deref()) {
if (isMutable())
(*this)->destroyAll(); (*this)->destroyAll();
Data::deallocate(d); Data::deallocate(d);
} }
@ -143,7 +137,7 @@ public:
bool isNull() const noexcept bool isNull() const noexcept
{ {
return d == Data::sharedNull(); return !ptr;
} }
T *data() noexcept { return ptr; } T *data() noexcept { return ptr; }
@ -171,8 +165,8 @@ public:
bool detach() bool detach()
{ {
if (d->needsDetach()) { if (needsDetach()) {
QPair<Data *, T *> copy = clone(d->detachFlags()); QPair<Data *, T *> copy = clone(detachFlags());
QArrayDataPointer old(d, ptr, size); QArrayDataPointer old(d, ptr, size);
d = copy.first; d = copy.first;
ptr = copy.second; ptr = copy.second;
@ -183,36 +177,35 @@ public:
} }
// forwards from QArrayData // forwards from QArrayData
size_t allocatedCapacity() noexcept { return d->allocatedCapacity(); } size_t allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
size_t constAllocatedCapacity() const noexcept { return d->constAllocatedCapacity(); } size_t constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
int refCounterValue() const noexcept { return d->refCounterValue(); } void ref() noexcept { if (d) d->ref(); }
bool ref() noexcept { return d->ref(); } bool deref() noexcept { return !d || d->deref(); }
bool deref() noexcept { return d->deref(); } bool isMutable() const noexcept { return d; }
bool isMutable() const noexcept { return d->isMutable(); } bool isStatic() const noexcept { return !d; }
bool isStatic() const noexcept { return d->isStatic(); } bool isShared() const noexcept { return !d || d->isShared(); }
bool isShared() const noexcept { return d->isShared(); }
bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; } bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
bool needsDetach() const noexcept { return d->needsDetach(); } bool needsDetach() const noexcept { return !d || d->needsDetach(); }
size_t detachCapacity(size_t newSize) const noexcept { return d->detachCapacity(newSize); } size_t detachCapacity(size_t newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
typename Data::ArrayOptions &flags() noexcept { return reinterpret_cast<typename Data::ArrayOptions &>(d->flags); } const typename Data::ArrayOptions flags() const noexcept { return d ? typename Data::ArrayOption(d->flags) : Data::DefaultAllocationFlags; }
typename Data::ArrayOptions flags() const noexcept { return typename Data::ArrayOption(d->flags); } void setFlag(typename Data::ArrayOptions f) { Q_ASSERT(d); d->flags |= f; }
typename Data::ArrayOptions detachFlags() const noexcept { return d->detachFlags(); } void clearFlag(typename Data::ArrayOptions f) { Q_ASSERT(d); d->flags &= ~f; }
typename Data::ArrayOptions cloneFlags() const noexcept { return d->cloneFlags(); } typename Data::ArrayOptions detachFlags() const noexcept { return d ? d->detachFlags() : Data::DefaultAllocationFlags; }
Data *d_ptr() { return d; } Data *d_ptr() { return d; }
private: private:
Q_REQUIRED_RESULT QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const Q_REQUIRED_RESULT QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
{ {
QPair<Data *, T *> pair = Data::allocate(d->detachCapacity(size), QPair<Data *, T *> pair = Data::allocate(detachCapacity(size), options);
options);
Q_CHECK_PTR(pair.first); Q_CHECK_PTR(pair.first);
QArrayDataPointer copy(pair.first, pair.second, 0); QArrayDataPointer copy(pair.first, pair.second, 0);
if (size) if (size)
copy->copyAppend(begin(), end()); copy->copyAppend(begin(), end());
pair.first = copy.d; pair.first = copy.d;
copy.d = Data::sharedNull(); copy.d = nullptr;
copy.ptr = nullptr;
return pair; return pair;
} }

View File

@ -428,6 +428,12 @@ public:
static inline QList<T> fromVector(const QList<T> &vector) { return vector; } static inline QList<T> fromVector(const QList<T> &vector) { return vector; }
inline QList<T> toVector() const { return *this; } inline QList<T> toVector() const { return *this; }
template<int N>
static QList<T> fromReadOnlyData(const T (&t)[N])
{
return QList<T>({ nullptr, const_cast<T *>(t), N });
}
}; };
#if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201606 #if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201606
@ -465,7 +471,7 @@ void QList<T>::reserve(int asize)
return; // already reserved, don't shrink return; // already reserved, don't shrink
if (!d->isShared()) { if (!d->isShared()) {
// accept current allocation, don't shrink // accept current allocation, don't shrink
d->flags() |= Data::CapacityReserved; d->setFlag(Data::CapacityReserved);
return; return;
} }
} }

View File

@ -2277,7 +2277,7 @@ void tst_QByteArray::literals()
QVERIFY(str.length() == 4); QVERIFY(str.length() == 4);
QCOMPARE(str.capacity(), 0); QCOMPARE(str.capacity(), 0);
QVERIFY(str == "abcd"); QVERIFY(str == "abcd");
QVERIFY(str.data_ptr()->isStatic()); QVERIFY(!str.data_ptr()->isMutable());
const char *s = str.constData(); const char *s = str.constData();
QByteArray str2 = str; QByteArray str2 = str;

View File

@ -4014,7 +4014,7 @@ void tst_QString::fromRawData()
{ {
const QChar ptr[] = { 0x1234, 0x0000 }; const QChar ptr[] = { 0x1234, 0x0000 };
QString cstr = QString::fromRawData(ptr, 1); QString cstr = QString::fromRawData(ptr, 1);
QVERIFY(cstr.isDetached()); QVERIFY(!cstr.isDetached());
QVERIFY(cstr.constData() == ptr); QVERIFY(cstr.constData() == ptr);
QVERIFY(cstr == QString(ptr, 1)); QVERIFY(cstr == QString(ptr, 1));
cstr.squeeze(); cstr.squeeze();
@ -4036,7 +4036,7 @@ void tst_QString::setRawData()
// This just tests the fromRawData() fallback // This just tests the fromRawData() fallback
QVERIFY(!cstr.isDetached()); QVERIFY(!cstr.isDetached());
cstr.setRawData(ptr, 1); cstr.setRawData(ptr, 1);
QVERIFY(cstr.isDetached()); QVERIFY(!cstr.isDetached());
QVERIFY(cstr.constData() == ptr); QVERIFY(cstr.constData() == ptr);
QVERIFY(cstr == QString(ptr, 1)); QVERIFY(cstr == QString(ptr, 1));
@ -6571,7 +6571,7 @@ void tst_QString::literals()
QVERIFY(str.length() == 4); QVERIFY(str.length() == 4);
QCOMPARE(str.capacity(), 0); QCOMPARE(str.capacity(), 0);
QVERIFY(str == QLatin1String("abcd")); QVERIFY(str == QLatin1String("abcd"));
QVERIFY(str.data_ptr()->isStatic()); QVERIFY(!str.data_ptr()->isMutable());
const QChar *s = str.constData(); const QChar *s = str.constData();
QString str2 = str; QString str2 = str;

View File

@ -153,7 +153,7 @@ public:
if (d->flags() & Data::CapacityReserved) if (d->flags() & Data::CapacityReserved)
return; return;
if (!d->isShared()) { if (!d->isShared()) {
d->flags() |= Data::CapacityReserved; d.setFlag(Data::CapacityReserved);
return; return;
} }
} }
@ -338,10 +338,9 @@ public:
d.detach(); d.detach();
} }
static SimpleVector fromRawData(const T *data, size_t size, static SimpleVector fromRawData(const T *data, size_t size)
QArrayData::ArrayOptions options = Data::DefaultRawFlags)
{ {
return SimpleVector(Data::fromRawData(data, size, options)); return SimpleVector(Data::fromRawData(data, size));
} }
private: private:

View File

@ -156,20 +156,15 @@ void tst_QArrayData::sharedNullEmpty()
void tst_QArrayData::simpleVector() void tst_QArrayData::simpleVector()
{ {
QArrayData data0 = { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }; int data[] = { 0, 1, 2, 3, 4, 5, 6 };
QStaticArrayData<int, 7> data1 = {
{ Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 },
{ 0, 1, 2, 3, 4, 5, 6 }
};
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
SimpleVector<int> v1; SimpleVector<int> v1;
SimpleVector<int> v2(v1); SimpleVector<int> v2(v1);
SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0), 0, 0); SimpleVector<int> v3(nullptr, nullptr, 0);
SimpleVector<int> v4(data1); SimpleVector<int> v4(nullptr, data, 0);
SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0), 0, 0); SimpleVector<int> v5(nullptr, data, 1);
SimpleVector<int> v6(data1); SimpleVector<int> v6(nullptr, data, 7);
SimpleVector<int> v7(10, 5); SimpleVector<int> v7(10, 5);
SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array));
@ -190,7 +185,7 @@ void tst_QArrayData::simpleVector()
QVERIFY(v2.isEmpty()); QVERIFY(v2.isEmpty());
QVERIFY(v3.isEmpty()); QVERIFY(v3.isEmpty());
QVERIFY(v4.isEmpty()); QVERIFY(v4.isEmpty());
QVERIFY(v5.isEmpty()); QVERIFY(!v5.isEmpty());
QVERIFY(!v6.isEmpty()); QVERIFY(!v6.isEmpty());
QVERIFY(!v7.isEmpty()); QVERIFY(!v7.isEmpty());
QVERIFY(!v8.isEmpty()); QVERIFY(!v8.isEmpty());
@ -199,7 +194,7 @@ void tst_QArrayData::simpleVector()
QCOMPARE(v2.size(), size_t(0)); QCOMPARE(v2.size(), size_t(0));
QCOMPARE(v3.size(), size_t(0)); QCOMPARE(v3.size(), size_t(0));
QCOMPARE(v4.size(), size_t(0)); QCOMPARE(v4.size(), size_t(0));
QCOMPARE(v5.size(), size_t(0)); QCOMPARE(v5.size(), size_t(1));
QCOMPARE(v6.size(), size_t(7)); QCOMPARE(v6.size(), size_t(7));
QCOMPARE(v7.size(), size_t(10)); QCOMPARE(v7.size(), size_t(10));
QCOMPARE(v8.size(), size_t(10)); QCOMPARE(v8.size(), size_t(10));
@ -248,13 +243,13 @@ void tst_QArrayData::simpleVector()
QVERIFY(v1 == v2); QVERIFY(v1 == v2);
QVERIFY(v1 == v3); QVERIFY(v1 == v3);
QVERIFY(v1 == v4); QVERIFY(v1 == v4);
QVERIFY(v1 == v5); QVERIFY(v1 != v5);
QVERIFY(!(v1 == v6)); QVERIFY(!(v1 == v6));
QVERIFY(v1 != v6); QVERIFY(v1 != v6);
QVERIFY(v4 != v6); QVERIFY(v4 != v6);
QVERIFY(v5 != v6); QVERIFY(v5 != v6);
QVERIFY(!(v1 != v5)); QVERIFY(!(v1 == v5));
QVERIFY(v1 < v6); QVERIFY(v1 < v6);
QVERIFY(!(v6 < v1)); QVERIFY(!(v6 < v1));
@ -428,17 +423,10 @@ void tst_QArrayData::simpleVectorReserve_data()
QTest::newRow("empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0); QTest::newRow("empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0);
QTest::newRow("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5); QTest::newRow("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5);
static const QStaticArrayData<int, 15> array = { static const int array[] =
{ Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } };
const QArrayDataPointerRef<int> p = {
static_cast<QTypedArrayData<int> *>(
const_cast<QArrayData *>(&array.header)),
const_cast<int *>(array.data),
sizeof(array.data) / sizeof(array.data[0]) };
QTest::newRow("static") << SimpleVector<int>(p) << size_t(0) << size_t(15); QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array, 15) << size_t(0) << size_t(15);
QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array.data, 15) << size_t(0) << size_t(15);
} }
void tst_QArrayData::simpleVectorReserve() void tst_QArrayData::simpleVectorReserve()
@ -519,7 +507,7 @@ void tst_QArrayData::allocate_data()
}; };
QArrayData *shared_empty; QArrayData *shared_empty;
QArrayData::allocate(&shared_empty, 1, alignof(QArrayData), 0); (void)QArrayData::allocate(&shared_empty, 1, alignof(QArrayData), 0);
QVERIFY(shared_empty); QVERIFY(shared_empty);
struct { struct {
@ -1180,15 +1168,15 @@ void fromRawData_impl()
{ {
// Default: Immutable, sharable // Default: Immutable, sharable
SimpleVector<T> raw = SimpleVector<T>::fromRawData(array, SimpleVector<T> raw = SimpleVector<T>::fromRawData(array,
sizeof(array)/sizeof(array[0]), QArrayData::DefaultRawFlags); sizeof(array)/sizeof(array[0]));
QCOMPARE(raw.size(), size_t(11)); QCOMPARE(raw.size(), size_t(11));
QCOMPARE((const T *)raw.constBegin(), array); QCOMPARE((const T *)raw.constBegin(), array);
QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0]))); QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
QVERIFY(!raw.isShared()); QVERIFY(raw.isShared());
QVERIFY(SimpleVector<T>(raw).isSharedWith(raw)); QVERIFY(SimpleVector<T>(raw).isSharedWith(raw));
QVERIFY(!raw.isShared()); QVERIFY(raw.isShared());
// Detach // Detach
QCOMPARE(raw.back(), T(11)); QCOMPARE(raw.back(), T(11));

View File

@ -336,6 +336,8 @@ private slots:
void emplaceWithElementFromTheSameContainer(); void emplaceWithElementFromTheSameContainer();
void emplaceWithElementFromTheSameContainer_data(); void emplaceWithElementFromTheSameContainer_data();
void fromReadOnlyData() const;
private: private:
template<typename T> void copyConstructor() const; template<typename T> void copyConstructor() const;
template<typename T> void add() const; template<typename T> void add() const;
@ -2830,5 +2832,53 @@ void tst_QList::emplaceConsistentWithStdVectorImpl() const
vecEq(qVec, stdVec); vecEq(qVec, stdVec);
} }
void tst_QList::fromReadOnlyData() const
{
{
QVector<char> d = QVector<char>::fromReadOnlyData("ABCDEFGHIJ");
QCOMPARE(d.size(), 10u + 1u);
for (int i = 0; i < 10; ++i)
QCOMPARE(d.data()[i], char('A' + i));
}
{
// wchar_t is not necessarily 2-bytes
QVector<wchar_t> d = QVector<wchar_t>::fromReadOnlyData(L"ABCDEFGHIJ");
QCOMPARE(d.size(), 10u + 1u);
for (int i = 0; i < 10; ++i)
QCOMPARE(d.data()[i], wchar_t('A' + i));
QVERIFY(d.isDetached());
}
{
const char data[] = "ABCDEFGHIJ";
const QVector<char> v = QVector<char>::fromReadOnlyData(data);
QVERIFY(v.constData() == data);
QVERIFY(!v.isEmpty());
QCOMPARE(v.size(), size_t(11));
// v.capacity() is unspecified, for now
QCOMPARE((void*)(const char*)(v.constBegin() + v.size()), (void*)(const char*)v.constEnd());
for (int i = 0; i < 10; ++i)
QCOMPARE(v[i], char('A' + i));
QCOMPARE(v[10], char('\0'));
}
{
struct LiteralType {
int value;
Q_DECL_CONSTEXPR LiteralType(int v = 0) : value(v) {}
};
const LiteralType literal[] = {LiteralType(0), LiteralType(1), LiteralType(2)};
const QVector<LiteralType> d = QVector<LiteralType>::fromReadOnlyData(literal);
QCOMPARE(d.size(), 3);
for (int i = 0; i < 3; ++i)
QCOMPARE(d.data()[i].value, i);
}
}
QTEST_MAIN(tst_QList) QTEST_MAIN(tst_QList)
#include "tst_qlist.moc" #include "tst_qlist.moc"

View File

@ -56,7 +56,7 @@ void tst_qmakelib::cleanupTestCase()
void tst_qmakelib::proString() void tst_qmakelib::proString()
{ {
QString qs1(QStringLiteral("this is a string")); QString qs1(QString::fromUtf8("this is a string"));
ProString s1(qs1); ProString s1(qs1);
QCOMPARE(s1.toQString(), QStringLiteral("this is a string")); QCOMPARE(s1.toQString(), QStringLiteral("this is a string"));