From 5f73f485563719e133c849c8dbd93877095d5ff5 Mon Sep 17 00:00:00 2001 From: Ahmad Samir Date: Sun, 11 Dec 2022 15:31:33 +0200 Subject: [PATCH] QString: optimize insert(qsizetype, QUtf8StringView) Utf8 data is variable-width, ideally we want to write characters at most once, so insert directly into the QString buffer if inserting at the end (by delegating to append(QUtf8SV)), and use an intermediate buffer to hold the converted data before inserting anywhere else. Task-number: QTBUG-108546 Change-Id: Iabfaeecaf34a1ba11946bd67951e69a45d954d6d Reviewed-by: Thiago Macieira --- src/corelib/text/qstring.cpp | 31 +++++++++++++++++-- .../auto/corelib/text/qstring/tst_qstring.cpp | 1 + 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index daa8f8d98b..b6c36e1b2f 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -2939,7 +2939,9 @@ QString &QString::operator=(QChar ch) defined. */ - +/*! \internal + T is a view or a container on/of QChar, char16_t, or char +*/ template static void insert_helper(QString &str, qsizetype i, T toInsert) { @@ -3021,7 +3023,32 @@ QString &QString::insert(qsizetype i, QLatin1StringView str) */ QString &QString::insert(qsizetype i, QUtf8StringView s) { - return insert(i, s.toString()); // ### optimize (QTBUG-108546) + auto insert_size = s.size(); + if (i < 0 || insert_size <= 0) + return *this; + + qsizetype difference = 0; + if (Q_UNLIKELY(i > d.size)) + difference = i - d.size; + + if (i >= d.size) { + d.detachAndGrow(QArrayData::GrowsAtEnd, difference + insert_size, nullptr, nullptr); + Q_CHECK_PTR(d.data()); + + if (difference > 0) + resize(i, u' '); + append(s); + } else { + // Optimal insertion of Utf8 data is at the end, anywhere else could + // potentially lead to moving characters twice if Utf8 data size + // (variable-width) is less than the equiavalent Utf16 data size + QVarLengthArray buffer(insert_size); // ### optimize (QTBUG-108546) + char16_t *b = QUtf8::convertToUnicode(buffer.data(), s); + buffer.resize(std::distance(buffer.begin(), b)); + insert_helper(*this, i, buffer); + } + + return *this; } /*! diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index ac3d4d0f85..ca20e2fe5a 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -2803,6 +2803,7 @@ void tst_QString::insert_data(DataOptions options) QTest::newRow("a.insert(1, ba)") << a << baC << 1 << (a + ba); QTest::newRow("ba.insert(1, a)") << ba << aC << 1 << (ba + a); QTest::newRow("ba.insert(2, b)") << ba << bC << 2 << (ba + b); + QTest::newRow("ba.insert(10, b)") << ba << bC << 10 << (ba + QString(10 - ba.size(), u' ') + b); QTest::newRow("null-insert-0-yumlaut") << null << yumlautC << 0 << yumlaut; QTest::newRow("empty-insert-0-yumlaut") << empty << yumlautC << 0 << yumlaut;