diff options
-rw-r--r-- | src/corelib/tools/qstring.cpp | 41 | ||||
-rw-r--r-- | src/corelib/tools/qstringiterator_p.h | 7 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qstring/tst_qstring.cpp | 16 |
3 files changed, 60 insertions, 4 deletions
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 6b15900031..d1b5327f5c 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -5666,10 +5666,36 @@ QString QString::rightJustified(int width, QChar fill, bool truncate) const */ namespace QUnicodeTables { +/*! + \internal + Converts the \a str string starting from the position pointed to by the \a + it iterator, using the Unicode case traits \c Traits, and returns the + result. The input string must not be empty (the convertCase function below + guarantees that). + + The string type \c{T} is also a template and is either \c{const QString} or + \c{QString}. This function can do both copy-conversion and in-place + conversion depending on the state of the \a str parameter: + \list + \li \c{T} is \c{const QString}: copy-convert + \li \c{T} is \c{QString} and its refcount != 1: copy-convert + \li \c{T} is \c{QString} and its refcount == 1: in-place convert + \endlist + + In copy-convert mode, the local variable \c{s} is detached from the input + \a str. In the in-place convert mode, \a str is in moved-from state (which + this function requires to be a valid, empty string) and \c{s} contains the + only copy of the string, without reallocation (thus, \a it is still valid). + + There's one pathological case left: when the in-place conversion needs to + reallocate memory to grow the buffer. In that case, we need to adjust the \a + it pointer. + */ template <typename Traits, typename T> Q_NEVER_INLINE static QString detachAndConvertCase(T &str, QStringIterator it) { + Q_ASSERT(!str.isEmpty()); QString s = qMove(str); // will copy if T is const QString QChar *pp = s.begin() + it.index(); // will detach if necessary uint uc = it.nextUnchecked(); @@ -5678,12 +5704,19 @@ static QString detachAndConvertCase(T &str, QStringIterator it) signed short caseDiff = Traits::caseDiff(prop); if (Q_UNLIKELY(Traits::caseSpecial(prop))) { - // slow path + // slow path: the string is growing const ushort *specialCase = specialCaseMap + caseDiff; ushort length = *specialCase++; - int pos = pp - s.constBegin(); - s.replace(pos, 1, reinterpret_cast<const QChar *>(specialCase), length); - pp = const_cast<QChar *>(s.constBegin()) + pos + length; + int inpos = it.index() - 1; + int outpos = pp - s.constBegin(); + + s.replace(outpos, 1, reinterpret_cast<const QChar *>(specialCase), length); + pp = const_cast<QChar *>(s.constBegin()) + outpos + length; + + // do we need to adjust the input iterator too? + // if it is pointing to s's data, str is empty + if (str.isEmpty()) + it = QStringIterator(s.constBegin(), inpos + length, s.constEnd()); } else if (QChar::requiresSurrogates(uc)) { *pp++ = QChar::highSurrogate(uc + caseDiff); *pp++ = QChar::lowSurrogate(uc + caseDiff); diff --git a/src/corelib/tools/qstringiterator_p.h b/src/corelib/tools/qstringiterator_p.h index 06b05de833..1c098a314d 100644 --- a/src/corelib/tools/qstringiterator_p.h +++ b/src/corelib/tools/qstringiterator_p.h @@ -69,6 +69,13 @@ public: { } + inline explicit QStringIterator(const QChar *begin, int idx, const QChar *end) + : i(begin), + pos(begin + idx), + e(end) + { + } + inline QString::const_iterator position() const { return pos; diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index d18aa9f5f3..cc9f250be9 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -2144,6 +2144,22 @@ void tst_QString::toUpper() QCOMPARE( QString("`abyz{").toUpper(), QString("`ABYZ{")); QCOMPARE( QString(1, QChar(0xdf)).toUpper(), QString("SS")); + { + QString s = QString::fromUtf8("Gro\xc3\x9fstra\xc3\x9f""e"); + + // call lvalue-ref version, mustn't change the original + QCOMPARE(s.toUpper(), QString("GROSSSTRASSE")); + QCOMPARE(s, QString::fromUtf8("Gro\xc3\x9fstra\xc3\x9f""e")); + + // call rvalue-ref while shared (the original mustn't change) + QString copy = s; + QCOMPARE(qMove(copy).toUpper(), QString("GROSSSTRASSE")); + QCOMPARE(s, QString::fromUtf8("Gro\xc3\x9fstra\xc3\x9f""e")); + + // call rvalue-ref version on detached case + copy.clear(); + QCOMPARE(qMove(s).toUpper(), QString("GROSSSTRASSE")); + } QString lower, upper; lower += QChar(QChar::highSurrogate(0x10428)); |