XML/QDom: speedup encodeText()

The code copied the whole string, then replaced parts inline, at
the cost of relocating everything beyond, at each replacement.
Instead, copy character by character (in chunks where possible)
and append replacements as we skip what they replace.

Task-number: QTBUG-127549
Change-Id: I368482859ed0c4127f1eec2919183711b5488ada
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
(cherry picked from commit 2ce08e3671b8d18b0284447e5908ce15e6e8f80f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Christian Ehrlicher 2024-08-06 22:39:44 +02:00 committed by Qt Cherry-pick Bot
parent e0b2258b03
commit 225e235cf9

View File

@ -3611,47 +3611,57 @@ static QString encodeText(const QString &str,
const bool performAVN = false, const bool performAVN = false,
const bool encodeEOLs = false) const bool encodeEOLs = false)
{ {
QString retval(str); QString retval;
int len = retval.size(); qsizetype start = 0;
int i = 0; auto appendToOutput = [&](qsizetype cur, const auto &replacement)
{
if (start < cur) {
retval.reserve(str.size() + replacement.size());
retval.append(QStringView(str).first(cur).sliced(start));
}
// Skip over str[cur], replaced by replacement
start = cur + 1;
retval.append(replacement);
};
while (i < len) { const qsizetype len = str.size();
const QChar ati(retval.at(i)); for (qsizetype cur = 0; cur < len; ++cur) {
switch (str[cur].unicode()) {
if (ati == u'<') { case u'<':
retval.replace(i, 1, "&lt;"_L1); appendToOutput(cur, "&lt;"_L1);
len += 3; break;
i += 4; case u'"':
} else if (encodeQuotes && (ati == u'"')) { if (encodeQuotes)
retval.replace(i, 1, "&quot;"_L1); appendToOutput(cur, "&quot;"_L1);
len += 5; break;
i += 6; case u'&':
} else if (ati == u'&') { appendToOutput(cur, "&amp;"_L1);
retval.replace(i, 1, "&amp;"_L1); break;
len += 4; case u'>':
i += 5; if (cur >= 2 && str[cur - 1] == u']' && str[cur - 2] == u']')
} else if (ati == u'>' && i >= 2 && retval[i - 1] == u']' && retval[i - 2] == u']') { appendToOutput(cur, "&gt;"_L1);
retval.replace(i, 1, "&gt;"_L1); break;
len += 3; case u'\r':
i += 4; if (performAVN || encodeEOLs)
} else if (performAVN && appendToOutput(cur, "&#xd;"_L1); // \r == 0x0d
(ati == QChar(0xA) || break;
ati == QChar(0xD) || case u'\n':
ati == QChar(0x9))) { if (performAVN)
const QString replacement(u"&#x"_s + QString::number(ati.unicode(), 16) + u';'); appendToOutput(cur, "&#xa;"_L1); // \n == 0x0a
retval.replace(i, 1, replacement); break;
i += replacement.size(); case u'\t':
len += replacement.size() - 1; if (performAVN)
} else if (encodeEOLs && ati == QChar(0xD)) { appendToOutput(cur, "&#x9;"_L1); // \t == 0x09
retval.replace(i, 1, "&#xd;"_L1); // Replace a single 0xD with a ref for 0xD break;
len += 4; default:
i += 5; break;
} else {
++i;
} }
} }
if (start > 0) {
retval.append(QStringView(str).first(len).sliced(start));
return retval; return retval;
}
return str;
} }
void QDomAttrPrivate::save(QTextStream& s, int, int) const void QDomAttrPrivate::save(QTextStream& s, int, int) const