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