QString: Fix in-place toUpper/Lower when there's size expansion
When that happens, we need to detach (in-place conversion won't work), so we recurse back into the same function, but the template version that does detaching. Task-number: QTBUG-49181 Change-Id: Idba8c29717f34c70a58fffff1412fea3acc95f98 Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>bb10
parent
7c05a0cc99
commit
f29b6943f0
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue