QXmlStreamWriter: prepare for port to QAnyStringView

The UTF-8 support in Qt is still lacking, so QUtf8StringView doesn't,
yet, have the likes of contains(), endsWith(), etc that the existing
QString code uses in Q_ASSERTs.

Provide free functions that work for UTF-8 haystacks and ASCII needles
by falling back to QByteArrayView or QLatin1StringView.

Also break a replace() use into a series of indexOf() + chunked
write(). This is rather expensive for QString, so port the
writeCDATA() function that uses this to QAnyStringView already, ahead
of the bulk of the changes in Mate's follow-up patch.

Task-number: QTBUG-103302
Change-Id: Ic66261740817ede2600b78a383cf667a31df7bfc
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Tatiana Borisova <tatiana.borisova@qt.io>
This commit is contained in:
Marc Mutz 2022-12-08 19:20:48 +01:00
parent 9addd9a89d
commit bbb01309b4
3 changed files with 94 additions and 12 deletions

View File

@ -368,6 +368,17 @@ void QXmlStreamReader::addData(const char *data)
#endif // QT_CONFIG(xmlstreamreader)
#if QT_CONFIG(xmlstreamwriter)
#include "qxmlstream.h"
void QXmlStreamWriter::writeCDATA(const QString &text)
{
writeCDATA(qToAnyStringViewIgnoringNull(text));
}
#endif // QT_CONFIG(xmlstreamwriter)
// inlined API
#include "qfloat16.h"
#include "qstring.h"

View File

@ -44,8 +44,65 @@ auto reversed(Range &r)
template <typename Range>
void reversed(const Range &&) = delete;
// implementation of missing QUtf8StringView methods for ASCII-only needles:
auto transform(QLatin1StringView haystack, char needle)
{
struct R { QLatin1StringView haystack; char16_t needle; };
return R{haystack, uchar(needle)};
}
auto transform(QStringView haystack, char needle)
{
struct R { QStringView haystack; char16_t needle; };
return R{haystack, uchar(needle)};
}
auto transform(QUtf8StringView haystack, char needle)
{
struct R { QByteArrayView haystack; char needle; };
return R{haystack, needle};
}
[[maybe_unused]]
auto transform(QLatin1StringView haystack, QLatin1StringView needle)
{
struct R { QLatin1StringView haystack; QLatin1StringView needle; };
return R{haystack, needle};
}
[[maybe_unused]]
auto transform(QStringView haystack, QLatin1StringView needle)
{
struct R { QStringView haystack; QLatin1StringView needle; };
return R{haystack, needle};
}
[[maybe_unused]]
auto transform(QUtf8StringView haystack, QLatin1StringView needle)
{
struct R { QLatin1StringView haystack; QLatin1StringView needle; };
return R{QLatin1StringView{QByteArrayView{haystack}}, needle};
}
#define WRAP(method, Needle) \
auto method (QAnyStringView s, Needle needle) noexcept \
{ \
return s.visit([needle](auto s) { \
auto r = transform(s, needle); \
return r.haystack. method (r.needle); \
}); \
} \
/*end*/
WRAP(count, char)
WRAP(contains, char)
WRAP(contains, QLatin1StringView)
WRAP(endsWith, char)
WRAP(indexOf, QLatin1StringView)
} // unnamed namespace
/*!
\enum QXmlStreamReader::TokenType
@ -3216,7 +3273,7 @@ void QXmlStreamWriter::writeAttribute(const QString &qualifiedName, const QStrin
{
Q_D(QXmlStreamWriter);
Q_ASSERT(d->inStartElement);
Q_ASSERT(qualifiedName.count(u':') <= 1);
Q_ASSERT(count(qualifiedName, ':') <= 1);
d->write(" ");
d->write(qualifiedName);
d->write("=\"");
@ -3236,7 +3293,7 @@ void QXmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString
{
Q_D(QXmlStreamWriter);
Q_ASSERT(d->inStartElement);
Q_ASSERT(!name.contains(u':'));
Q_ASSERT(!contains(name, ':'));
QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true);
d->write(" ");
if (!namespaceDeclaration.prefix.isEmpty()) {
@ -3295,15 +3352,26 @@ void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
This function mainly exists for completeness. Normally you should
not need use it, because writeCharacters() automatically escapes all
non-content characters.
\note In Qt versions prior to 6.5, this function took QString, not
QAnyStringView.
*/
void QXmlStreamWriter::writeCDATA(const QString &text)
void QXmlStreamWriter::writeCDATA(QAnyStringView text)
{
Q_D(QXmlStreamWriter);
d->finishStartElement();
QString copy(text);
copy.replace("]]>"_L1, "]]]]><![CDATA[>"_L1);
d->write("<![CDATA[");
d->write(copy);
while (!text.isEmpty()) {
const auto idx = indexOf(text, "]]>"_L1);
if (idx < 0)
break; // no forbidden sequence found
d->write(text.first(idx));
d->write("]]" // text[idx, idx + 2)
"]]><![CDATA[" // escape sequence to separate ]] and >
">"); // text[idx + 2, idx + 3)
text = text.sliced(idx + 3); // skip over "]]>"
}
d->write(text); // write remainder
d->write("]]>");
}
@ -3329,7 +3397,7 @@ void QXmlStreamWriter::writeCharacters(const QString &text)
void QXmlStreamWriter::writeComment(const QString &text)
{
Q_D(QXmlStreamWriter);
Q_ASSERT(!text.contains("--"_L1) && !text.endsWith(u'-'));
Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
if (!d->finishStartElement(false) && d->autoFormatting)
d->indent(d->tagStack.size());
d->write("<!--");
@ -3362,7 +3430,7 @@ void QXmlStreamWriter::writeDTD(const QString &dtd)
void QXmlStreamWriter::writeEmptyElement(const QString &qualifiedName)
{
Q_D(QXmlStreamWriter);
Q_ASSERT(qualifiedName.count(u':') <= 1);
Q_ASSERT(count(qualifiedName, ':') <= 1);
d->writeStartElement(QString(), qualifiedName);
d->inEmptyElement = true;
}
@ -3378,7 +3446,7 @@ void QXmlStreamWriter::writeEmptyElement(const QString &qualifiedName)
void QXmlStreamWriter::writeEmptyElement(const QString &namespaceUri, const QString &name)
{
Q_D(QXmlStreamWriter);
Q_ASSERT(!name.contains(u':'));
Q_ASSERT(!contains(name, ':'));
d->writeStartElement(namespaceUri, name);
d->inEmptyElement = true;
}
@ -3544,7 +3612,7 @@ void QXmlStreamWriter::writeDefaultNamespace(const QString &namespaceUri)
void QXmlStreamWriter::writeProcessingInstruction(const QString &target, const QString &data)
{
Q_D(QXmlStreamWriter);
Q_ASSERT(!data.contains("?>"_L1));
Q_ASSERT(!contains(data, "?>"_L1));
if (!d->finishStartElement(false) && d->autoFormatting)
d->indent(d->tagStack.size());
d->write("<?");
@ -3618,7 +3686,7 @@ void QXmlStreamWriter::writeStartDocument(const QString &version, bool standalon
void QXmlStreamWriter::writeStartElement(const QString &qualifiedName)
{
Q_D(QXmlStreamWriter);
Q_ASSERT(qualifiedName.count(u':') <= 1);
Q_ASSERT(count(qualifiedName, ':') <= 1);
d->writeStartElement(QString(), qualifiedName);
}
@ -3634,7 +3702,7 @@ void QXmlStreamWriter::writeStartElement(const QString &qualifiedName)
void QXmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name)
{
Q_D(QXmlStreamWriter);
Q_ASSERT(!name.contains(u':'));
Q_ASSERT(!contains(name, ':'));
d->writeStartElement(namespaceUri, name);
}

View File

@ -344,7 +344,9 @@ public:
void writeAttribute(const QXmlStreamAttribute& attribute);
void writeAttributes(const QXmlStreamAttributes& attributes);
#if QT_CORE_REMOVED_SINCE(6,5)
void writeCDATA(const QString &text);
#endif
void writeCharacters(const QString &text);
void writeComment(const QString &text);
@ -355,6 +357,7 @@ public:
void writeTextElement(const QString &qualifiedName, const QString &text);
void writeTextElement(const QString &namespaceUri, const QString &name, const QString &text);
void writeCDATA(QAnyStringView text);
void writeEndDocument();
void writeEndElement();