summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2022-01-12 12:49:02 -0800
committerThiago Macieira <thiago.macieira@intel.com>2022-01-17 19:56:41 -0800
commit86b346823e114ac0b6b1c2ab6065ce53b1393de0 (patch)
tree567c2eb6c50caca1c438d855b14f53de0b9ab8bd
parenta59e7361714d50687d82a2d9abae9e95825a23b6 (diff)
QString::toLatin1_helper_inplace: simplify the code
Hot function, so help the compiler out. In particular, this removes any touch to the ref count, so there are no atomic operations or dead code leading to memory allocations and deallocations. Change-Id: I0e5f6bec596a4a78bd3bfffd16c9a0fbd8dd2c12 Reviewed-by: Marc Mutz <marc.mutz@qt.io>
-rw-r--r--src/corelib/text/qbytearray.h8
-rw-r--r--src/corelib/text/qstring.cpp34
-rw-r--r--src/corelib/tools/qarraydatapointer.h22
3 files changed, 43 insertions, 21 deletions
diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h
index 8989eb27a6..dbbbd2f178 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 23927b0e84..57bed1240f 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;
-
- // Move the d pointer over to the bytearray.
- // Kids, avert your eyes. Don't try this at home.
+ // This relies on the fact that we use QArrayData for everything behind the scenes
- // 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<QByteArray::Data *>(s.d.d_ptr()), reinterpret_cast<char *>(s.d.data()), length);
- ba_d.ref();
- s.clear();
+ // 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<uchar *>(sdata), sdata, length + 1);
- char *ddata = ba_d.data();
+ // 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<char>();
- // multiply the allocated capacity by sizeof(char16_t)
- ba_d.d_ptr()->alloc *= sizeof(char16_t);
+ // 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());
- // do the in-place conversion
- qt_to_latin1(reinterpret_cast<uchar *>(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 4d83b890cc..dda3cf13eb 100644
--- a/src/corelib/tools/qarraydatapointer.h
+++ b/src/corelib/tools/qarraydatapointer.h
@@ -168,6 +168,28 @@ public:
/*! \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 <typename X> QArrayDataPointer<X> reinterpreted() &&
+ {
+ if (sizeof(T) != sizeof(X)) {
+ Q_ASSERT(!d->isShared());
+ d->alloc = d->alloc * sizeof(T) / sizeof(X);
+ }
+ auto od = reinterpret_cast<QTypedArrayData<X> *>(std::exchange(d, nullptr));
+ auto optr = reinterpret_cast<X *>(std::exchange(ptr, nullptr));
+ return { od, optr, std::exchange(size, 0) };
+ }
+
+ /*! \internal
+
Detaches this (optionally) and grows to accommodate the free space for
\a n elements at the required side. The side is determined from \a pos.