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:
parent
2e51686746
commit
76004502ba
@ -156,7 +156,8 @@ QString ProString::toQString() 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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -270,7 +270,6 @@ class ProStringRoUser
|
||||
public:
|
||||
ProStringRoUser(QString &rs)
|
||||
{
|
||||
Q_ASSERT(rs.isDetached() || rs.isEmpty());
|
||||
m_rs = &rs;
|
||||
}
|
||||
ProStringRoUser(const ProString &ps, QString &rs)
|
||||
|
@ -67,6 +67,8 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
const char QByteArray::_empty = '\0';
|
||||
|
||||
// ASCII case system, used by QByteArray::to{Upper,Lower}() and qstr(n)icmp():
|
||||
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)
|
||||
{
|
||||
Q_ASSERT(str1);
|
||||
Q_ASSERT(len1 >= 0);
|
||||
Q_ASSERT(len2 >= -1);
|
||||
const uchar *s1 = reinterpret_cast<const uchar *>(str1);
|
||||
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)
|
||||
return len1 == 0 ? 0 : 1;
|
||||
|
||||
@ -1694,7 +1703,7 @@ void QByteArray::expand(int i)
|
||||
QByteArray QByteArray::nulTerminated() const
|
||||
{
|
||||
// is this fromRawData?
|
||||
if (!IS_RAW_DATA(d))
|
||||
if (d.isMutable())
|
||||
return *this; // no, then we're sure we're zero terminated
|
||||
|
||||
QByteArray copy(*this);
|
||||
@ -1725,7 +1734,7 @@ QByteArray QByteArray::nulTerminated() const
|
||||
|
||||
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;
|
||||
} else if (ba.size() != 0) {
|
||||
QByteArray tmp = *this;
|
||||
@ -1818,7 +1827,7 @@ QByteArray &QByteArray::prepend(char ch)
|
||||
|
||||
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;
|
||||
} else if (ba.size() != 0) {
|
||||
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
|
||||
{
|
||||
if (isEmpty())
|
||||
return !ba.size() ? 0 : -1;
|
||||
const int ol = ba.size();
|
||||
if (ol == 1)
|
||||
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
|
||||
{
|
||||
if (isEmpty())
|
||||
return (str && *str) ? -1 : 0;
|
||||
const int ol = qstrlen(str);
|
||||
if (ol == 1)
|
||||
return lastIndexOf(*str, from);
|
||||
@ -3484,6 +3497,11 @@ T toIntegral_helper(const char *data, bool *ok, int base)
|
||||
base = 10;
|
||||
}
|
||||
#endif
|
||||
if (!data) {
|
||||
if (ok)
|
||||
*ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we select the right overload by the last, unused parameter
|
||||
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
|
||||
\a data array. The bytes are \e not copied. The QByteArray will
|
||||
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()
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
|
@ -54,6 +54,11 @@
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
|
||||
#ifndef QT5_NULL_STRINGS
|
||||
// ### Should default to 0 in Qt 6.0
|
||||
#define QT5_NULL_STRINGS 1
|
||||
#endif
|
||||
|
||||
#ifdef truncate
|
||||
#error qbytearray.h must be included before any header file that defines truncate
|
||||
#endif
|
||||
@ -117,16 +122,7 @@ class QDataStream;
|
||||
using QByteArrayData = QArrayDataPointer<char>;
|
||||
|
||||
# define QByteArrayLiteral(str) \
|
||||
([]() -> QByteArray { \
|
||||
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); \
|
||||
}()) \
|
||||
(QByteArray(QByteArrayData(nullptr, const_cast<char *>(str), sizeof(str) - 1))) \
|
||||
/**/
|
||||
|
||||
class Q_CORE_EXPORT QByteArray
|
||||
@ -137,6 +133,7 @@ private:
|
||||
typedef QTypedArrayData<char> Data;
|
||||
|
||||
DataPointer d;
|
||||
static const char _empty;
|
||||
public:
|
||||
|
||||
enum Base64Option {
|
||||
@ -158,7 +155,7 @@ public:
|
||||
IllegalPadding,
|
||||
};
|
||||
|
||||
inline QByteArray() noexcept;
|
||||
inline constexpr QByteArray() noexcept;
|
||||
QByteArray(const char *, int size = -1);
|
||||
QByteArray(int size, char c);
|
||||
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(qulonglong, int base = 10);
|
||||
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;
|
||||
Q_REQUIRED_RESULT static FromBase64Result fromBase64Encoding(QByteArray &&base64, Base64Options options = Base64Encoding);
|
||||
@ -449,7 +449,7 @@ private:
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QByteArray::Base64Options)
|
||||
|
||||
inline QByteArray::QByteArray() noexcept {}
|
||||
inline constexpr QByteArray::QByteArray() noexcept {}
|
||||
inline QByteArray::~QByteArray() {}
|
||||
|
||||
inline char QByteArray::at(int i) const
|
||||
@ -466,11 +466,21 @@ inline QByteArray::operator const void *() const
|
||||
{ return data(); }
|
||||
#endif
|
||||
inline char *QByteArray::data()
|
||||
{ detach(); return d.data(); }
|
||||
{
|
||||
detach();
|
||||
Q_ASSERT(d.data());
|
||||
return d.data();
|
||||
}
|
||||
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
|
||||
{ return d.data(); }
|
||||
{ return data(); }
|
||||
inline void QByteArray::detach()
|
||||
{ if (d->needsDetach()) reallocData(uint(size()) + 1u, d->detachFlags()); }
|
||||
inline bool QByteArray::isDetached() const
|
||||
@ -486,7 +496,7 @@ inline void QByteArray::reserve(int asize)
|
||||
if (d->needsDetach() || asize > capacity()) {
|
||||
reallocData(qMax(uint(size()), uint(asize)) + 1u, d->detachFlags() | Data::CapacityReserved);
|
||||
} else {
|
||||
d->flags() |= Data::CapacityReserved;
|
||||
d->setFlag(Data::CapacityReserved);
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,7 +507,7 @@ inline void QByteArray::squeeze()
|
||||
if (d->needsDetach() || size() < capacity()) {
|
||||
reallocData(uint(size()) + 1u, d->detachFlags() & ~Data::CapacityReserved);
|
||||
} else {
|
||||
d->flags() &= uint(~Data::CapacityReserved);
|
||||
d->clearFlag(Data::CapacityReserved);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
const char16_t QString::_empty = 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
DataPointer empty = { pair.first, pair.second, 0 };
|
||||
return QString(empty);
|
||||
return QString(std::move(empty));
|
||||
}
|
||||
case QContainerImplHelper::Full:
|
||||
return *this;
|
||||
@ -5066,7 +5068,7 @@ QString QString::fromLocal8Bit_helper(const char *str, int size)
|
||||
if (size == 0 || (!*str && size < 0)) {
|
||||
QPair<Data *, char16_t *> pair = Data::allocate(0);
|
||||
QString::DataPointer empty = { pair.first, pair.second, 0 };
|
||||
return QString(empty);
|
||||
return QString(std::move(empty));
|
||||
}
|
||||
QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
|
||||
return toUtf16(str, size);
|
||||
@ -8617,14 +8619,7 @@ bool QString::isRightToLeft() const
|
||||
*/
|
||||
QString QString::fromRawData(const QChar *unicode, int size)
|
||||
{
|
||||
QString::DataPointer x;
|
||||
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);
|
||||
return QString(Data::fromRawData(const_cast<char16_t *>(reinterpret_cast<const char16_t *>(unicode)), size));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -275,7 +275,7 @@ class Q_CORE_EXPORT QString
|
||||
public:
|
||||
typedef QStringPrivate DataPointer;
|
||||
|
||||
inline QString() noexcept;
|
||||
inline constexpr QString() noexcept;
|
||||
explicit QString(const QChar *unicode, int size = -1);
|
||||
QString(QChar c);
|
||||
QString(int size, QChar c);
|
||||
@ -902,7 +902,7 @@ public:
|
||||
{ return QStringView(*this).isValidUtf16(); }
|
||||
|
||||
QString(int size, Qt::Initialization);
|
||||
explicit QString(DataPointer dd) : d(dd) {}
|
||||
explicit QString(DataPointer &&dd) : d(std::move(dd)) {}
|
||||
|
||||
private:
|
||||
#if defined(QT_NO_CAST_FROM_ASCII)
|
||||
@ -915,6 +915,7 @@ private:
|
||||
#endif
|
||||
|
||||
DataPointer d;
|
||||
static const char16_t _empty;
|
||||
|
||||
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
|
||||
{ return d.size == 0; }
|
||||
inline const QChar *QString::unicode() const
|
||||
{ return reinterpret_cast<const QChar*>(d.data()); }
|
||||
{ return data(); }
|
||||
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()
|
||||
{ detach(); return reinterpret_cast<QChar*>(d.data()); }
|
||||
{
|
||||
detach();
|
||||
Q_ASSERT(d.data());
|
||||
return reinterpret_cast<QChar *>(d.data());
|
||||
}
|
||||
inline const QChar *QString::constData() const
|
||||
{ return reinterpret_cast<const QChar*>(d.data()); }
|
||||
{ return data(); }
|
||||
inline void QString::detach()
|
||||
{ if (d->needsDetach()) reallocData(d.size + 1u); }
|
||||
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);
|
||||
}
|
||||
|
||||
inline QString::QString() noexcept {}
|
||||
inline constexpr QString::QString() noexcept {}
|
||||
inline QString::~QString() {}
|
||||
|
||||
inline void QString::reserve(int asize)
|
||||
@ -1116,7 +1127,7 @@ inline void QString::reserve(int asize)
|
||||
reallocData(uint(qMax(asize, size())) + 1u);
|
||||
|
||||
// we're not shared anymore, for sure
|
||||
d->flags() |= Data::CapacityReserved;
|
||||
d->setFlag(Data::CapacityReserved);
|
||||
}
|
||||
|
||||
inline void QString::squeeze()
|
||||
@ -1127,7 +1138,7 @@ inline void QString::squeeze()
|
||||
reallocData(uint(d.size) + 1u);
|
||||
|
||||
// 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)
|
||||
|
@ -62,18 +62,9 @@ static_assert(sizeof(qunicodechar) == 2,
|
||||
|
||||
#define QT_UNICODE_LITERAL(str) u"" str
|
||||
#define QStringLiteral(str) \
|
||||
([]() noexcept -> QString { \
|
||||
enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \
|
||||
static const QArrayData qstring_literal = { \
|
||||
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); \
|
||||
}()) \
|
||||
(QString(QStringPrivate(nullptr, \
|
||||
reinterpret_cast<char16_t *>(const_cast<qunicodechar *>(QT_UNICODE_LITERAL(str))), \
|
||||
sizeof(QT_UNICODE_LITERAL(str))/2 - 1))) \
|
||||
/**/
|
||||
|
||||
using QStringPrivate = QArrayDataPointer<char16_t>;
|
||||
|
@ -232,25 +232,15 @@ void *QArrayData::allocate(QArrayData **dptr, size_t objectSize, size_t alignmen
|
||||
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 *>
|
||||
QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
|
||||
size_t objectSize, size_t capacity, ArrayOptions options) noexcept
|
||||
{
|
||||
Q_ASSERT(data);
|
||||
Q_ASSERT(data->isMutable());
|
||||
Q_ASSERT(!data->isShared());
|
||||
Q_ASSERT(!data || !data->isShared());
|
||||
|
||||
size_t headerSize = sizeof(QArrayData);
|
||||
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;
|
||||
QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize)));
|
||||
if (header) {
|
||||
|
@ -143,14 +143,6 @@ struct Q_CORE_EXPORT QArrayData
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayOptions cloneFlags() const
|
||||
{
|
||||
ArrayOptions result = DefaultAllocationFlags;
|
||||
if (flags & CapacityReserved)
|
||||
result |= CapacityReserved;
|
||||
return result;
|
||||
}
|
||||
|
||||
Q_REQUIRED_RESULT
|
||||
#if defined(Q_CC_GNU)
|
||||
__attribute__((__malloc__))
|
||||
@ -159,8 +151,6 @@ struct Q_CORE_EXPORT QArrayData
|
||||
size_t capacity, ArrayOptions options = DefaultAllocationFlags) noexcept;
|
||||
Q_REQUIRED_RESULT static QPair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer,
|
||||
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,
|
||||
size_t alignment) noexcept;
|
||||
|
||||
@ -229,36 +219,14 @@ struct QTypedArrayData
|
||||
QArrayData::deallocate(data, sizeof(T), alignof(AlignmentDummy));
|
||||
}
|
||||
|
||||
static QArrayDataPointerRef<T> fromRawData(const T *data, size_t n,
|
||||
ArrayOptions options = DefaultRawFlags)
|
||||
static QArrayDataPointerRef<T> fromRawData(const T *data, size_t n)
|
||||
{
|
||||
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -271,33 +239,11 @@ struct QTypedArrayData
|
||||
|
||||
// Hide array inside a lambda
|
||||
#define Q_ARRAY_LITERAL(Type, ...) \
|
||||
([]() -> QArrayDataPointerRef<Type> { \
|
||||
/* 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<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]) }; \
|
||||
\
|
||||
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 }; \
|
||||
([]() -> QArrayDataPointerRef<Type> { \
|
||||
static Type const data[] = { __VA_ARGS__ }; \
|
||||
enum { Size = sizeof(data) / sizeof(data[0]) }; \
|
||||
return { nullptr, const_cast<Type *>(data), Size }; \
|
||||
}())
|
||||
/**/
|
||||
|
||||
namespace QtPrivate {
|
||||
|
@ -134,7 +134,7 @@ public:
|
||||
|
||||
void destroyAll() // Call from destructors, ONLY!
|
||||
{
|
||||
Q_ASSERT(this->isMutable());
|
||||
Q_ASSERT(this->d);
|
||||
Q_ASSERT(this->d->ref_.loadRelaxed() == 0);
|
||||
|
||||
// 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
|
||||
{
|
||||
Q_ASSERT(this->isMutable());
|
||||
Q_ASSERT(this->d);
|
||||
// As this is to be called only from destructor, it doesn't need to be
|
||||
// exception safe; size not updated.
|
||||
|
||||
|
@ -59,18 +59,18 @@ public:
|
||||
|
||||
typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type;
|
||||
|
||||
QArrayDataPointer() noexcept
|
||||
: d(Data::sharedNull()), ptr(Data::sharedNullData()), size(0)
|
||||
constexpr QArrayDataPointer() noexcept
|
||||
: d(nullptr), ptr(nullptr), size(0)
|
||||
{
|
||||
}
|
||||
|
||||
QArrayDataPointer(const QArrayDataPointer &other) noexcept
|
||||
: 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))
|
||||
{
|
||||
}
|
||||
@ -96,54 +96,48 @@ public:
|
||||
QArrayDataPointer(QArrayDataPointer &&other) noexcept
|
||||
: d(other.d), ptr(other.ptr), size(other.size)
|
||||
{
|
||||
other.d = Data::sharedNull();
|
||||
other.ptr = Data::sharedNullData();
|
||||
other.d = nullptr;
|
||||
other.ptr = nullptr;
|
||||
other.size = 0;
|
||||
}
|
||||
|
||||
QArrayDataPointer &operator=(QArrayDataPointer &&other) noexcept
|
||||
{
|
||||
QArrayDataPointer moved(std::move(other));
|
||||
this->swap(moved);
|
||||
this->swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataOps &operator*() noexcept
|
||||
{
|
||||
Q_ASSERT(d);
|
||||
return *static_cast<DataOps *>(this);
|
||||
}
|
||||
|
||||
DataOps *operator->() noexcept
|
||||
{
|
||||
Q_ASSERT(d);
|
||||
return static_cast<DataOps *>(this);
|
||||
}
|
||||
|
||||
const DataOps &operator*() const noexcept
|
||||
{
|
||||
Q_ASSERT(d);
|
||||
return *static_cast<const DataOps *>(this);
|
||||
}
|
||||
|
||||
const DataOps *operator->() const noexcept
|
||||
{
|
||||
Q_ASSERT(d);
|
||||
return static_cast<const DataOps *>(this);
|
||||
}
|
||||
|
||||
~QArrayDataPointer()
|
||||
{
|
||||
if (!deref()) {
|
||||
if (isMutable())
|
||||
(*this)->destroyAll();
|
||||
(*this)->destroyAll();
|
||||
Data::deallocate(d);
|
||||
}
|
||||
}
|
||||
|
||||
bool isNull() const noexcept
|
||||
{
|
||||
return d == Data::sharedNull();
|
||||
return !ptr;
|
||||
}
|
||||
|
||||
T *data() noexcept { return ptr; }
|
||||
@ -171,8 +165,8 @@ public:
|
||||
|
||||
bool detach()
|
||||
{
|
||||
if (d->needsDetach()) {
|
||||
QPair<Data *, T *> copy = clone(d->detachFlags());
|
||||
if (needsDetach()) {
|
||||
QPair<Data *, T *> copy = clone(detachFlags());
|
||||
QArrayDataPointer old(d, ptr, size);
|
||||
d = copy.first;
|
||||
ptr = copy.second;
|
||||
@ -183,36 +177,35 @@ public:
|
||||
}
|
||||
|
||||
// forwards from QArrayData
|
||||
size_t allocatedCapacity() noexcept { return d->allocatedCapacity(); }
|
||||
size_t constAllocatedCapacity() const noexcept { return d->constAllocatedCapacity(); }
|
||||
int refCounterValue() const noexcept { return d->refCounterValue(); }
|
||||
bool ref() noexcept { return d->ref(); }
|
||||
bool deref() noexcept { return d->deref(); }
|
||||
bool isMutable() const noexcept { return d->isMutable(); }
|
||||
bool isStatic() const noexcept { return d->isStatic(); }
|
||||
bool isShared() const noexcept { return d->isShared(); }
|
||||
size_t allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
|
||||
size_t constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
|
||||
void ref() noexcept { if (d) d->ref(); }
|
||||
bool deref() noexcept { return !d || d->deref(); }
|
||||
bool isMutable() const noexcept { return d; }
|
||||
bool isStatic() const noexcept { return !d; }
|
||||
bool isShared() const noexcept { return !d || d->isShared(); }
|
||||
bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
|
||||
bool needsDetach() const noexcept { return d->needsDetach(); }
|
||||
size_t detachCapacity(size_t newSize) const noexcept { return d->detachCapacity(newSize); }
|
||||
typename Data::ArrayOptions &flags() noexcept { return reinterpret_cast<typename Data::ArrayOptions &>(d->flags); }
|
||||
typename Data::ArrayOptions flags() const noexcept { return typename Data::ArrayOption(d->flags); }
|
||||
typename Data::ArrayOptions detachFlags() const noexcept { return d->detachFlags(); }
|
||||
typename Data::ArrayOptions cloneFlags() const noexcept { return d->cloneFlags(); }
|
||||
bool needsDetach() const noexcept { return !d || d->needsDetach(); }
|
||||
size_t detachCapacity(size_t newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
|
||||
const typename Data::ArrayOptions flags() const noexcept { return d ? typename Data::ArrayOption(d->flags) : Data::DefaultAllocationFlags; }
|
||||
void setFlag(typename Data::ArrayOptions f) { Q_ASSERT(d); d->flags |= f; }
|
||||
void clearFlag(typename Data::ArrayOptions f) { Q_ASSERT(d); d->flags &= ~f; }
|
||||
typename Data::ArrayOptions detachFlags() const noexcept { return d ? d->detachFlags() : Data::DefaultAllocationFlags; }
|
||||
|
||||
Data *d_ptr() { return d; }
|
||||
|
||||
private:
|
||||
Q_REQUIRED_RESULT QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
|
||||
{
|
||||
QPair<Data *, T *> pair = Data::allocate(d->detachCapacity(size),
|
||||
options);
|
||||
QPair<Data *, T *> pair = Data::allocate(detachCapacity(size), options);
|
||||
Q_CHECK_PTR(pair.first);
|
||||
QArrayDataPointer copy(pair.first, pair.second, 0);
|
||||
if (size)
|
||||
copy->copyAppend(begin(), end());
|
||||
|
||||
pair.first = copy.d;
|
||||
copy.d = Data::sharedNull();
|
||||
copy.d = nullptr;
|
||||
copy.ptr = nullptr;
|
||||
return pair;
|
||||
}
|
||||
|
||||
|
@ -428,6 +428,12 @@ public:
|
||||
|
||||
static inline QList<T> fromVector(const QList<T> &vector) { return vector; }
|
||||
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
|
||||
@ -465,7 +471,7 @@ void QList<T>::reserve(int asize)
|
||||
return; // already reserved, don't shrink
|
||||
if (!d->isShared()) {
|
||||
// accept current allocation, don't shrink
|
||||
d->flags() |= Data::CapacityReserved;
|
||||
d->setFlag(Data::CapacityReserved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2277,7 +2277,7 @@ void tst_QByteArray::literals()
|
||||
QVERIFY(str.length() == 4);
|
||||
QCOMPARE(str.capacity(), 0);
|
||||
QVERIFY(str == "abcd");
|
||||
QVERIFY(str.data_ptr()->isStatic());
|
||||
QVERIFY(!str.data_ptr()->isMutable());
|
||||
|
||||
const char *s = str.constData();
|
||||
QByteArray str2 = str;
|
||||
|
@ -4014,7 +4014,7 @@ void tst_QString::fromRawData()
|
||||
{
|
||||
const QChar ptr[] = { 0x1234, 0x0000 };
|
||||
QString cstr = QString::fromRawData(ptr, 1);
|
||||
QVERIFY(cstr.isDetached());
|
||||
QVERIFY(!cstr.isDetached());
|
||||
QVERIFY(cstr.constData() == ptr);
|
||||
QVERIFY(cstr == QString(ptr, 1));
|
||||
cstr.squeeze();
|
||||
@ -4036,7 +4036,7 @@ void tst_QString::setRawData()
|
||||
// This just tests the fromRawData() fallback
|
||||
QVERIFY(!cstr.isDetached());
|
||||
cstr.setRawData(ptr, 1);
|
||||
QVERIFY(cstr.isDetached());
|
||||
QVERIFY(!cstr.isDetached());
|
||||
QVERIFY(cstr.constData() == ptr);
|
||||
QVERIFY(cstr == QString(ptr, 1));
|
||||
|
||||
@ -6571,7 +6571,7 @@ void tst_QString::literals()
|
||||
QVERIFY(str.length() == 4);
|
||||
QCOMPARE(str.capacity(), 0);
|
||||
QVERIFY(str == QLatin1String("abcd"));
|
||||
QVERIFY(str.data_ptr()->isStatic());
|
||||
QVERIFY(!str.data_ptr()->isMutable());
|
||||
|
||||
const QChar *s = str.constData();
|
||||
QString str2 = str;
|
||||
|
@ -153,7 +153,7 @@ public:
|
||||
if (d->flags() & Data::CapacityReserved)
|
||||
return;
|
||||
if (!d->isShared()) {
|
||||
d->flags() |= Data::CapacityReserved;
|
||||
d.setFlag(Data::CapacityReserved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -338,10 +338,9 @@ public:
|
||||
d.detach();
|
||||
}
|
||||
|
||||
static SimpleVector fromRawData(const T *data, size_t size,
|
||||
QArrayData::ArrayOptions options = Data::DefaultRawFlags)
|
||||
static SimpleVector fromRawData(const T *data, size_t size)
|
||||
{
|
||||
return SimpleVector(Data::fromRawData(data, size, options));
|
||||
return SimpleVector(Data::fromRawData(data, size));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -156,20 +156,15 @@ void tst_QArrayData::sharedNullEmpty()
|
||||
|
||||
void tst_QArrayData::simpleVector()
|
||||
{
|
||||
QArrayData data0 = { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 };
|
||||
QStaticArrayData<int, 7> data1 = {
|
||||
{ Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6 }
|
||||
};
|
||||
|
||||
int data[] = { 0, 1, 2, 3, 4, 5, 6 };
|
||||
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
SimpleVector<int> v1;
|
||||
SimpleVector<int> v2(v1);
|
||||
SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0), 0, 0);
|
||||
SimpleVector<int> v4(data1);
|
||||
SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0), 0, 0);
|
||||
SimpleVector<int> v6(data1);
|
||||
SimpleVector<int> v3(nullptr, nullptr, 0);
|
||||
SimpleVector<int> v4(nullptr, data, 0);
|
||||
SimpleVector<int> v5(nullptr, data, 1);
|
||||
SimpleVector<int> v6(nullptr, data, 7);
|
||||
SimpleVector<int> v7(10, 5);
|
||||
SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array));
|
||||
|
||||
@ -190,7 +185,7 @@ void tst_QArrayData::simpleVector()
|
||||
QVERIFY(v2.isEmpty());
|
||||
QVERIFY(v3.isEmpty());
|
||||
QVERIFY(v4.isEmpty());
|
||||
QVERIFY(v5.isEmpty());
|
||||
QVERIFY(!v5.isEmpty());
|
||||
QVERIFY(!v6.isEmpty());
|
||||
QVERIFY(!v7.isEmpty());
|
||||
QVERIFY(!v8.isEmpty());
|
||||
@ -199,7 +194,7 @@ void tst_QArrayData::simpleVector()
|
||||
QCOMPARE(v2.size(), size_t(0));
|
||||
QCOMPARE(v3.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(v7.size(), size_t(10));
|
||||
QCOMPARE(v8.size(), size_t(10));
|
||||
@ -248,13 +243,13 @@ void tst_QArrayData::simpleVector()
|
||||
QVERIFY(v1 == v2);
|
||||
QVERIFY(v1 == v3);
|
||||
QVERIFY(v1 == v4);
|
||||
QVERIFY(v1 == v5);
|
||||
QVERIFY(v1 != v5);
|
||||
QVERIFY(!(v1 == v6));
|
||||
|
||||
QVERIFY(v1 != v6);
|
||||
QVERIFY(v4 != v6);
|
||||
QVERIFY(v5 != v6);
|
||||
QVERIFY(!(v1 != v5));
|
||||
QVERIFY(!(v1 == v5));
|
||||
|
||||
QVERIFY(v1 < v6);
|
||||
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("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5);
|
||||
|
||||
static const QStaticArrayData<int, 15> array = {
|
||||
{ Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 },
|
||||
{ 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]) };
|
||||
static const int array[] =
|
||||
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
QTest::newRow("static") << SimpleVector<int>(p) << size_t(0) << size_t(15);
|
||||
QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array.data, 15) << size_t(0) << size_t(15);
|
||||
QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array, 15) << size_t(0) << size_t(15);
|
||||
}
|
||||
|
||||
void tst_QArrayData::simpleVectorReserve()
|
||||
@ -519,7 +507,7 @@ void tst_QArrayData::allocate_data()
|
||||
};
|
||||
|
||||
QArrayData *shared_empty;
|
||||
QArrayData::allocate(&shared_empty, 1, alignof(QArrayData), 0);
|
||||
(void)QArrayData::allocate(&shared_empty, 1, alignof(QArrayData), 0);
|
||||
QVERIFY(shared_empty);
|
||||
|
||||
struct {
|
||||
@ -1180,15 +1168,15 @@ void fromRawData_impl()
|
||||
{
|
||||
// Default: Immutable, sharable
|
||||
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((const T *)raw.constBegin(), array);
|
||||
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(!raw.isShared());
|
||||
QVERIFY(raw.isShared());
|
||||
|
||||
// Detach
|
||||
QCOMPARE(raw.back(), T(11));
|
||||
|
@ -336,6 +336,8 @@ private slots:
|
||||
void emplaceWithElementFromTheSameContainer();
|
||||
void emplaceWithElementFromTheSameContainer_data();
|
||||
|
||||
void fromReadOnlyData() const;
|
||||
|
||||
private:
|
||||
template<typename T> void copyConstructor() const;
|
||||
template<typename T> void add() const;
|
||||
@ -2830,5 +2832,53 @@ void tst_QList::emplaceConsistentWithStdVectorImpl() const
|
||||
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)
|
||||
#include "tst_qlist.moc"
|
||||
|
@ -56,7 +56,7 @@ void tst_qmakelib::cleanupTestCase()
|
||||
|
||||
void tst_qmakelib::proString()
|
||||
{
|
||||
QString qs1(QStringLiteral("this is a string"));
|
||||
QString qs1(QString::fromUtf8("this is a string"));
|
||||
|
||||
ProString s1(qs1);
|
||||
QCOMPARE(s1.toQString(), QStringLiteral("this is a string"));
|
||||
|
Loading…
Reference in New Issue
Block a user