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>
This commit is contained in:
Thiago Macieira 2022-01-12 12:49:02 -08:00
parent a59e736171
commit 86b346823e
3 changed files with 43 additions and 21 deletions

View File

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

View File

@ -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<uchar *>(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<char>();
// 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();
// 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<uchar *>(ddata), data, length);
ddata[length] = '\0';
return QByteArray(ba_d);
return QByteArray(std::move(ba_d));
}
/*!

View File

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