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
{
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:
ProStringRoUser(QString &rs)
{
Q_ASSERT(rs.isDetached() || rs.isEmpty());
m_rs = &rs;
}
ProStringRoUser(const ProString &ps, QString &rs)

View File

@ -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

View File

@ -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);
}
}

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);
}
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));
}
/*!

View File

@ -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)

View File

@ -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>;

View File

@ -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) {

View File

@ -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());
}
};
////////////////////////////////////////////////////////////////////////////////
@ -272,32 +240,10 @@ 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__ }; \
static Type const 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 }; \
return { nullptr, const_cast<Type *>(data), Size }; \
}())
/**/
namespace QtPrivate {

View File

@ -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.

View File

@ -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,46 +96,40 @@ 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();
Data::deallocate(d);
}
@ -143,7 +137,7 @@ public:
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;
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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:

View File

@ -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));

View File

@ -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"

View File

@ -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"));