diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index 8989eb27a67..dbbbd2f1788 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -473,10 +473,10 @@ public: bool isNull() const noexcept; inline DataPointer &data_ptr() { return d; } - explicit inline QByteArray(const DataPointer &dd) - : d(dd) - { - } +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + explicit inline QByteArray(const DataPointer &dd) : d(dd) {} +#endif + explicit inline QByteArray(DataPointer &&dd) : d(std::move(dd)) {} private: void reallocData(qsizetype alloc, QArrayData::AllocationOption option); diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 23927b0e840..57bed1240f8 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -5269,6 +5269,7 @@ QByteArray QtPrivate::convertToLatin1(QStringView string) return qt_convert_to_latin1(string); } +Q_NEVER_INLINE static QByteArray qt_convert_to_latin1(QStringView string) { if (Q_UNLIKELY(string.isNull())) @@ -5290,27 +5291,26 @@ QByteArray QString::toLatin1_helper_inplace(QString &s) // We can return our own buffer to the caller. // Conversion to Latin-1 always shrinks the buffer by half. - const char16_t *data = s.d.data(); - qsizetype length = s.d.size; + // This relies on the fact that we use QArrayData for everything behind the scenes - // Move the d pointer over to the bytearray. + // First, do the in-place conversion. Since isDetached() == true, the data + // was allocated by QArrayData, so the null terminator must be there. + qsizetype length = s.size(); + char16_t *sdata = s.d->data(); + Q_ASSERT(sdata[length] == u'\0'); + qt_to_latin1(reinterpret_cast(sdata), sdata, length + 1); + + // Move the internals over to the byte array. // Kids, avert your eyes. Don't try this at home. + auto ba_d = std::move(s.d).reinterpreted(); - // this relies on the fact that we use QArrayData for everything behind the scenes which has the same layout - static_assert(sizeof(QByteArray::DataPointer) == sizeof(QString::DataPointer), "sizes have to be equal"); - QByteArray::DataPointer ba_d(reinterpret_cast(s.d.d_ptr()), reinterpret_cast(s.d.data()), length); - ba_d.ref(); - s.clear(); + // Some sanity checks + Q_ASSERT(ba_d.d->allocatedCapacity() >= ba_d.size); + Q_ASSERT(s.isNull()); + Q_ASSERT(s.isEmpty()); + Q_ASSERT(s.constData() == QString().constData()); - char *ddata = ba_d.data(); - - // multiply the allocated capacity by sizeof(char16_t) - ba_d.d_ptr()->alloc *= sizeof(char16_t); - - // do the in-place conversion - qt_to_latin1(reinterpret_cast(ddata), data, length); - ddata[length] = '\0'; - return QByteArray(ba_d); + return QByteArray(std::move(ba_d)); } /*! diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 4d83b890cc6..dda3cf13eb1 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -166,6 +166,28 @@ public: reallocateAndGrow(QArrayData::GrowsAtEnd, 0, old); } + /*! \internal + + Reinterprets the data of this QArrayDataPointer to type X. It's the + caller's responsibility to ensure that the data contents are valid and + properly aligned, particularly if T and X are not trivial types (i.e, + don't do that). The current size is kept and the allocated capacity is + updated to account for the difference in the element type's size. + + This is used in QString::fromLatin1 to perform in-place conversion of + QString to QByteArray. + */ + template QArrayDataPointer reinterpreted() && + { + if (sizeof(T) != sizeof(X)) { + Q_ASSERT(!d->isShared()); + d->alloc = d->alloc * sizeof(T) / sizeof(X); + } + auto od = reinterpret_cast *>(std::exchange(d, nullptr)); + auto optr = reinterpret_cast(std::exchange(ptr, nullptr)); + return { od, optr, std::exchange(size, 0) }; + } + /*! \internal Detaches this (optionally) and grows to accommodate the free space for