Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:data-parser
++
++ #include "QtCore/qxmlstream.h"
++
++ #if QT_CONFIG(xmlstream)
++
++ #include "qxmlutils_p.h"
++
++ #include <qdebug.h>
++ #include <qfile.h>
++ #include <stdio.h>
++ #include <QtCore/qspan.h>
++ #include <qstringconverter.h>
++ #include <qstack.h>
++ #include <qbuffer.h>
++ #include <qscopeguard.h>
++ #include <qcoreapplication.h>
++ #include <QtCore/private/qduplicatetracker_p.h>
++ #include <private/qoffsetstringarray_p.h>
++ #include <private/qtools_p.h>
++
++ #include <iterator>
++ #include "qxmlstream_p.h"
++ #include "qxmlstreamparser_p.h"
++ #include <private/qstringconverter_p.h>
++ #include <private/qstringiterator_p.h>
++
++ #include <algorithm>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace QtPrivate;
++ using namespace Qt::StringLiterals;
++ using namespace QtMiscUtils;
++
++ constexpr uint StreamEOF = ~0U;
++
++ namespace {
++ template <typename Range>
++ auto reversed(Range &r)
++ {
++ struct R {
++ Range *r;
++ auto begin() { return std::make_reverse_iterator(std::end(*r)); }
++ auto end() { return std::make_reverse_iterator(std::begin(*r)); }
++ };
++
++ return R{&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};
++ }
++
++ auto transform(QLatin1StringView haystack, QLatin1StringView needle)
++ {
++ struct R { QLatin1StringView haystack; QLatin1StringView needle; };
++ return R{haystack, needle};
++ }
++
++ auto transform(QStringView haystack, QLatin1StringView needle)
++ {
++ struct R { QStringView haystack; QLatin1StringView needle; };
++ return R{haystack, needle};
++ }
++
++ 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)
++
++ // ### begin move someplace central and complete
++ template <auto V> struct Value { static constexpr auto value = V; };
++ template <typename T> struct EncodingForHelper;
++ template <> struct EncodingForHelper<QStringView> : Value<QStringDecoder::Utf16> {};
++ template <> struct EncodingForHelper<QLatin1StringView> : Value<QStringDecoder::Latin1> {};
++ template <bool B> struct EncodingForHelper<QBasicUtf8StringView<B>> : Value<QStringDecoder::Utf8> {};
++ template <typename T>
++ constexpr auto encodingFor = EncodingForHelper<T>::value;
++ // ### end move someplace central
++
++ } // unnamed namespace
++
++ /*!
++ \enum QXmlStreamReader::TokenType
++
++ This enum specifies the type of token the reader just read.
++
++ \value NoToken The reader has not yet read anything.
++
++ \value Invalid An error has occurred, reported in error() and
++ errorString().
++
++ \value StartDocument The reader reports the XML version number in
++ documentVersion(), and the encoding as specified in the XML
++ document in documentEncoding(). If the document is declared
++ standalone, isStandaloneDocument() returns \c true; otherwise it
++ returns \c false.
++
++ \value EndDocument The reader reports the end of the document.
++
++ \value StartElement The reader reports the start of an element
++ with namespaceUri() and name(). Empty elements are also reported
++ as StartElement, followed directly by EndElement. The convenience
++ function readElementText() can be called to concatenate all
++ content until the corresponding EndElement. Attributes are
++ reported in attributes(), namespace declarations in
++ namespaceDeclarations().
++
++ \value EndElement The reader reports the end of an element with
++ namespaceUri() and name().
++
++ \value Characters The reader reports characters in text(). If the
++ characters are all white-space, isWhitespace() returns \c true. If
++ the characters stem from a CDATA section, isCDATA() returns \c true.
++
++ \value Comment The reader reports a comment in text().
++
++ \value DTD The reader reports a DTD in text(), notation
++ declarations in notationDeclarations(), and entity declarations in
++ entityDeclarations(). Details of the DTD declaration are reported
++ in dtdName(), dtdPublicId(), and dtdSystemId().
++
++ \value EntityReference The reader reports an entity reference that
++ could not be resolved. The name of the reference is reported in
++ name(), the replacement text in text().
++
++ \value ProcessingInstruction The reader reports a processing
++ instruction in processingInstructionTarget() and
++ processingInstructionData().
++ */
++
++ /*!
++ \enum QXmlStreamReader::ReadElementTextBehaviour
++
++ This enum specifies the different behaviours of readElementText().
++
++ \value ErrorOnUnexpectedElement Raise an UnexpectedElementError and return
++ what was read so far when a child element is encountered.
++
++ \value IncludeChildElements Recursively include the text from child elements.
++
++ \value SkipChildElements Skip child elements.
++
++ \since 4.6
++ */
++
++ /*!
++ \enum QXmlStreamReader::Error
++
++ This enum specifies different error cases
++
++ \value NoError No error has occurred.
++
++ \value CustomError A custom error has been raised with
++ raiseError()
++
++ \value NotWellFormedError The parser internally raised an error
++ due to the read XML not being well-formed.
++
++ \value PrematureEndOfDocumentError The input stream ended before a
++ well-formed XML document was parsed. Recovery from this error is
++ possible if more XML arrives in the stream, either by calling
++ addData() or by waiting for it to arrive on the device().
++
++ \value UnexpectedElementError The parser encountered an element
++ or token that was different to those it expected.
++
++ */
++
++ /*!
++ \class QXmlStreamEntityResolver
++ \inmodule QtCore
++ \reentrant
++ \since 4.4
++
++ \brief The QXmlStreamEntityResolver class provides an entity
++ resolver for a QXmlStreamReader.
++
++ \ingroup xml-tools
++
++ Use to inform QXmlStreamReader how to expand entities not
++ \l{QXmlStreamReader::entityDeclarations()}{declared in the internal-set of
++ the DTD}.
++
++ \target sec-con-QXmlStreamEntityResolver
++ \section1 Security Considerations
++
++ \target sec-con-QXmlStreamEntityResolver-cycles
++ \section2 Entity Cycles
++
++ You must take care to avoid resolving entities in cycles, because
++ QXmlStreamReader only detects and rejects cycles in
++ \l{QXmlStreamReader::entityDeclarations()}{entities defined in the DTD},
++ not in entities expanded from an implementation of the
++ QXmlStreamEntityResolver interface.
++
++ This is by design: QXmlStreamReader puts the work on the implementation of
++ a concrete QXmlStreamEntityResolver. For static mappings, like
++ \l{https://www.w3.org/TR/xml-entity-names/}{XML Entity Definitions for
++ Characters}, this is trivially guaranteed, so running some tracking in the
++ background would just slow things down for everyone.
++
++ Things get interesting when you read entity definitions from external
++ input, including untrusted sources. In this case, \e{your implementation}
++ must ensure the expansions it uses as input are cycle-free. This is a
++ graph operation} that can be implemented in linear time and space and can
++ be performed up front at load time, so it doesn't affect
++ resolveUndeclaredEntity() performance.
++
++ \target sec-con-QXmlStreamEntityResolver-expansion-limit
++ \section2 Entity Expansion Limit
++
++ At the moment, entities resolved by this class do not count against
++ QXmlStreamReader::entityExpansionLimit(). This may change in future
++ versions of Qt.
++
++ For the time being, you need to enforce some upper expansion limit
++ yourself, if you read entity definitions from external input. After cycle
++ detection,
++ \l{https://en.wikipedia.org/wiki/Longest_path_problem#Acyclic_graphs}{longest-path
++ calculation} is a simple graph operation that can be implemented in linear
++ time and space and can be performed up front at load time, so it, too,
++ doesn't affect resolveUndeclaredEntity() performance.
++
++ \sa QXmlStreamReader::setEntityResolver()
++ */
++
++ /*!
++ Destroys the entity resolver.
++ */
++ QXmlStreamEntityResolver::~QXmlStreamEntityResolver()
++ {
++ }
++
++ /*!
++ \internal
++
++ This function is a stub for later functionality.
++ */
++ QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/)
++ {
++ Q_UNIMPLEMENTED();
++ return QString();
++ }
++
++
++ /*!
++ Reimplement this function to resolve the undeclared entity \a name and
++ return its replacement text. If the entity is unknown to the entity
++ resolver, return a \l{QString::isNull()}{null string}: \c{QString()}. This
++ will raise an error in QXmlStreamreader. An empty, but non-null string is
++ considered a valid expansion of the entity, and will not cause an error.
++
++ The default implementation always returns \c{QString()}.
++ */
++
++ QString QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/)
++ {
++ return QString();
++ }
++
++ #if QT_CONFIG(xmlstreamreader)
++
++ QString QXmlStreamReaderPrivate::resolveUndeclaredEntity(const QString &name)
++ {
++ if (entityResolver)
++ return entityResolver->resolveUndeclaredEntity(name);
++ return QString();
++ }
++
++
++
++ /*!
++ \since 4.4
++
++ Makes \a resolver the new entityResolver().
++
++ The stream reader does \e not take ownership of the resolver. It's
++ the callers responsibility to ensure that the resolver is valid
++ during the entire life-time of the stream reader object, or until
++ another resolver or \nullptr is set.
++
++ \sa entityResolver()
++ */
++ void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver)
++ {
++ Q_D(QXmlStreamReader);
++ d->entityResolver = resolver;
++ }
++
++ /*!
++ \since 4.4
++
++ Returns the entity resolver, or \nullptr if there is no entity resolver.
++
++ \sa setEntityResolver()
++ */
++ QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->entityResolver;
++ }
++
++
++
++ /*!
++ \class QXmlStreamReader
++ \inmodule QtCore
++ \reentrant
++ \since 4.3
++
++ \brief The QXmlStreamReader class provides a fast parser for reading
++ well-formed XML 1.0 documents via a simple streaming API.
++
++
++ \ingroup xml-tools
++
++ \ingroup qtserialization
++
++ QXmlStreamReader provides a simple streaming API to parse well-formed
++ XML 1.0 documents. It is an alternative to first loading the complete
++ XML into a DOM tree (see \l QDomDocument). QXmlStreamReader reads data
++ either from a QIODevice (see setDevice()), or from a raw QByteArray
++ (see addData()).
++
++ \note QXmlStreamReader supports only XML version 1.0. Documents declaring
++ any other version, such as "1.1", will result in a parsing error.
++
++ Qt provides QXmlStreamWriter for writing XML.
++
++ The basic concept of a stream reader is to report an XML document as
++ a stream of tokens, similar to SAX. The main difference between
++ QXmlStreamReader and SAX is \e how these XML tokens are reported.
++ With SAX, the application must provide handlers (callback functions)
++ that receive so-called XML \e events from the parser at the parser's
++ convenience. With QXmlStreamReader, the application code itself
++ drives the loop and pulls \e tokens from the reader, one after
++ another, as it needs them. This is done by calling readNext(), where
++ the reader reads from the input stream until it completes the next
++ token, at which point it returns the tokenType(). A set of
++ convenient functions including isStartElement() and text() can then
++ be used to examine the token to obtain information about what has
++ been read. The big advantage of this \e pulling approach is the
++ possibility to build recursive descent parsers with it, meaning you
++ can split your XML parsing code easily into different methods or
++ classes. This makes it easy to keep track of the application's own
++ state when parsing XML.
++
++ A typical loop with QXmlStreamReader looks like this:
++
++ \snippet code/src_corelib_xml_qxmlstream.cpp 0
++
++
++ QXmlStreamReader is a non-validating, forward-only XML 1.0 parser
++ for well-formed documents. It does \e not process external parsed
++ entities or perform DTD validation.
++ As long as no error occurs, the application can rely on the
++ following guarantees:
++ \list
++ \li The XML content satisfies the W3C's criteria for
++ well-formed XML 1.0
++ \li References to internal entities are replaced with the correct
++ replacement text.
++ \li Attributes are normalized or added according to the
++ internal \l DTD subset.
++ \li Tokens are provided in the correct order for a well-formed
++ document.
++ \li A \l StartDocument token (if present) appears before all
++ other elements, aside from comments and processing instructions.
++ \li At most one DOCTYPE element (a token of type \l DTD) is present,
++ and if so, it appears before any other content (aside from
++ StartDocument, comments, and processing instructions).
++ \endlist
++
++ In particular, once any token of type \l StartElement, \l EndElement,
++ \l Characters, \l EntityReference or \l EndDocument is seen, no
++ tokens of type StartDocument or DTD will be seen. If one is present in
++ the input stream, out of order, an error is raised.
++
++ \note The token types \l Comment and \l ProcessingInstruction may appear
++ anywhere in the stream.
++
++ If an error occurs while parsing, atEnd() and hasError() return
++ true, and error() returns the error that occurred. The functions
++ errorString(), lineNumber(), columnNumber(), and characterOffset()
++ are for constructing an appropriate error or warning message. To
++ simplify application code, QXmlStreamReader contains a raiseError()
++ mechanism that lets you raise custom errors that trigger the same
++ error handling described.
++
++ The \l{QXmlStream Bookmarks Example} illustrates how to use the
++ recursive descent technique to read an XML bookmark file (XBEL) with
++ a stream reader.
++
++ \section1 Namespaces
++
++ QXmlStream understands and resolves XML namespaces. E.g. in case of
++ a StartElement, namespaceUri() returns the namespace the element is
++ in, and name() returns the element's \e local name. The combination
++ of namespaceUri and name uniquely identifies an element. If a
++ namespace prefix was not declared in the XML entities parsed by the
++ reader, the namespaceUri is empty.
++
++ If you parse XML data that does not utilize namespaces according to
++ the XML specification or doesn't use namespaces at all, you can use
++ the element's qualifiedName() instead. A qualified name is the
++ element's prefix() followed by colon followed by the element's local
++ name() - exactly like the element appears in the raw XML data. Since
++ the mapping namespaceUri to prefix is neither unique nor universal,
++ qualifiedName() should be avoided for namespace-compliant XML data.
++
++ In order to parse standalone documents that do use undeclared
++ namespace prefixes, you can turn off namespace processing completely
++ with the \l namespaceProcessing property.
++
++ \section1 Incremental Parsing
++
++ QXmlStreamReader is an incremental parser. It can handle the case
++ where the document can't be parsed all at once because it arrives in
++ chunks (e.g. from multiple files, or over a network connection).
++ When the reader runs out of data before the complete document has
++ been parsed, it reports a PrematureEndOfDocumentError. When more
++ data arrives, either because of a call to addData() or because more
++ data is available through the network device(), the reader recovers
++ from the PrematureEndOfDocumentError error and continues parsing the
++ new data with the next call to readNext().
++
++ For example, if your application reads data from the network using a
++ \l{QNetworkAccessManager} {network access manager}, you would issue
++ a \l{QNetworkRequest} {network request} to the manager and receive a
++ \l{QNetworkReply} {network reply} in return. Since a QNetworkReply
++ is a QIODevice, you connect its \l{QIODevice::readyRead()}
++ {readyRead()} signal to a custom slot, e.g. \c{slotReadyRead()} in
++ the code snippet shown in the discussion for QNetworkAccessManager.
++ In this slot, you read all available data with
++ \l{QIODevice::readAll()} {readAll()} and pass it to the XML
++ stream reader using addData(). Then you call your custom parsing
++ function that reads the XML events from the reader.
++
++ \section1 Performance and Memory Consumption
++
++ QXmlStreamReader is memory-conservative by design, since it doesn't
++ store the entire XML document tree in memory, but only the current
++ token at the time it is reported. In addition, QXmlStreamReader
++ avoids the many small string allocations that it normally takes to
++ map an XML document to a convenient and Qt-ish API. It does this by
++ reporting all string data as QStringView rather than real QString
++ objects. Calling \l{QStringView::toString()}{toString()} on any of
++ those objects returns an equivalent real QString object.
++ */
++
++ Q_DECL_COLD_FUNCTION
++ static void warn_addData_with_device()
++ {
++ qWarning("QXmlStreamReader: addData() with device()");
++ }
++
++ /*!
++ Constructs a stream reader.
++
++ \sa setDevice(), addData()
++ */
++ QXmlStreamReader::QXmlStreamReader()
++ : d_ptr(new QXmlStreamReaderPrivate(this))
++ {
++ }
++
++ /*! Creates a new stream reader that reads from \a device.
++
++ \sa setDevice(), clear()
++ */
++ QXmlStreamReader::QXmlStreamReader(QIODevice *device)
++ : d_ptr(new QXmlStreamReaderPrivate(this))
++ {
++ setDevice(device);
++ }
++
++ /*!
++ \overload
++
++ \fn QXmlStreamReader::QXmlStreamReader(const QByteArray &data)
++
++ Creates a new stream reader that reads from \a data.
++
++ \sa addData(), clear(), setDevice()
++ */
++
++ /*!
++ \internal
++
++ Append a chunk \a data which uses \a enc as encoding.
++
++ Passing \l QStringDecoder::System as \a enc means that the encoding is
++ unknown and a document-global decoder should be used. Otherwise, a
++ chunk decoder with the specified encoding will be created and used, so
++ the document-global decoder will not be used and/or modified.
++ */
++ void QXmlStreamReaderPrivate::appendDataWithEncoding(const QByteArray &data,
++ QStringDecoder::Encoding enc)
++ {
++ if (tryAppendViewWithEncoding(data, enc))
++ return;
++ dataInfo.emplace_back(data, enc);
++ }
++
++ bool QXmlStreamReaderPrivate::tryAppendViewWithEncoding(QByteArrayView data, QStringDecoder::Encoding enc)
++ {
++ if (data.isEmpty())
++ return true;
++ // Joining the buffers might be useful for a stateful decoder, or when
++ // e == System, meaning that we have to try to guess the decoder
++ if (!dataInfo.empty()) {
++ auto &last = dataInfo.back();
++ if (last.encoding == enc) {
++ last.buffer.append(data);
++ return true;
++ }
++ }
++ return false;
++ }
++
++ void QXmlStreamReaderPrivate::appendViewWithEncoding(QByteArrayView data, QStringDecoder::Encoding enc)
++ {
++ if (tryAppendViewWithEncoding(data, enc))
++ return;
++ dataInfo.emplace_back(data.toByteArray(), enc);
++ }
++
++ /*!
++ Creates a new stream reader that reads from \a data.
++
++ \note In Qt versions prior to 6.5, this constructor was overloaded
++ for QString and \c {const char*}.
++
++ \sa addData(), clear(), setDevice()
++ */
++ QXmlStreamReader::QXmlStreamReader(QAnyStringView data)
++ : d_ptr(new QXmlStreamReaderPrivate(this))
++ {
++ addData(data);
++ }
++
++ /*!
++ \internal
++
++ Creates a new stream reader that reads from \a data.
++ Used by the weak constructor taking a QByteArray.
++ */
++ QXmlStreamReader::QXmlStreamReader(const QByteArray &data, PrivateConstructorTag)
++ : d_ptr(new QXmlStreamReaderPrivate(this))
++ {
++ Q_D(QXmlStreamReader);
++ d->appendDataWithEncoding(data, QStringDecoder::System);
++ }
++
++ /*!
++ Destructs the reader.
++ */
++ QXmlStreamReader::~QXmlStreamReader()
++ {
++ Q_D(QXmlStreamReader);
++ if (d->deleteDevice)
++ delete d->device;
++ }
++
++ /*! \fn bool QXmlStreamReader::hasError() const
++ Returns \c true if an error has occurred, otherwise \c false.
++
++ \sa errorString(), error()
++ */
++
++ /*!
++ Sets the current device to \a device. Setting the device resets
++ the stream to its initial state.
++
++ \sa device(), clear()
++ */
++ void QXmlStreamReader::setDevice(QIODevice *device)
++ {
++ Q_D(QXmlStreamReader);
++ if (d->deleteDevice) {
++ delete d->device;
++ d->deleteDevice = false;
++ }
++ d->device = device;
++ d->init();
++
++ }
++
++ /*!
++ Returns the current device associated with the QXmlStreamReader,
++ or \nullptr if no device has been assigned.
++
++ \sa setDevice()
++ */
++ QIODevice *QXmlStreamReader::device() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->device;
++ }
++
++ /*!
++ \overload
++
++ \fn void QXmlStreamReader::addData(const QByteArray &data)
++
++ Adds more \a data for the reader to read. This function does
++ nothing if the reader has a device().
++
++ \sa readNext(), clear()
++ */
++
++ static bool isDecoderForEncoding(const QStringDecoder &dec, QStringDecoder::Encoding enc)
++ {
++ if (!dec.isValid())
++ return false;
++
++ const auto decName = dec.name();
++ if (!decName || !*decName) // only match when non-empty
++ return false;
++
++ const auto encName = QStringConverter::nameForEncoding(enc);
++ return encName && strcmp(decName, encName) == 0;
++ }
++
++ /*!
++ Adds more \a data for the reader to read. This function does
++ nothing if the reader has a device().
++
++ \note In Qt versions prior to 6.5, this function was overloaded
++ for QString and \c {const char*}.
++
++ \sa readNext(), clear()
++ */
++ void QXmlStreamReader::addData(QAnyStringView data)
++ {
++ Q_D(QXmlStreamReader);
++ if (d->device)
++ return warn_addData_with_device();
++ data.visit([d](auto data) {
++ d->appendViewWithEncoding(as_bytes(QSpan(data)), encodingFor<decltype(data)>);
++ });
++ }
++
++ /*!
++ \internal
++
++ Adds more \a data for the reader to read. This function does
++ nothing if the reader has a device().
++ */
++ void QXmlStreamReader::addDataImpl(const QByteArray &data)
++ {
++ Q_D(QXmlStreamReader);
++ if (d->device)
++ return warn_addData_with_device();
++ d->appendDataWithEncoding(data, QStringDecoder::System);
++ }
++
++ /*!
++ Removes any device() or data from the reader and resets its
++ internal state to the initial state.
++
++ \sa addData()
++ */
++ void QXmlStreamReader::clear()
++ {
++ Q_D(QXmlStreamReader);
++ d->init();
++ if (d->device) {
++ if (d->deleteDevice)
++ delete d->device;
++ d->device = nullptr;
++ }
++ }
++
++ /*!
++ Returns \c true if the reader has read until the end of the XML
++ document, or if an error() has occurred and reading has been
++ aborted. Otherwise, it returns \c false.
++
++ When atEnd() and hasError() return true and error() returns
++ PrematureEndOfDocumentError, it means the XML has been well-formed
++ so far, but a complete XML document has not been parsed. The next
++ chunk of XML can be added with addData(), if the XML is being read
++ from a QByteArray, or by waiting for more data to arrive if the
++ XML is being read from a QIODevice. Either way, atEnd() will
++ return false once more data is available.
++
++ \sa hasError(), error(), device(), QIODevice::atEnd()
++ */
++ bool QXmlStreamReader::atEnd() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->atEnd
++ && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError)
++ || (d->type == QXmlStreamReader::EndDocument))) {
++ if (d->device)
++ return d->device->atEnd();
++ else
++ return d->dataInfo.empty();
++ }
++ return (d->atEnd || d->type == QXmlStreamReader::Invalid);
++ }
++
++
++ /*!
++ Reads the next token and returns its type.
++
++ With one exception, once an error() is reported by readNext(),
++ further reading of the XML stream is not possible. Then atEnd()
++ returns \c true, hasError() returns \c true, and this function returns
++ QXmlStreamReader::Invalid.
++
++ The exception is when error() returns PrematureEndOfDocumentError.
++ This error is reported when the end of an otherwise well-formed
++ chunk of XML is reached, but the chunk doesn't represent a complete
++ XML document. In that case, parsing \e can be resumed by calling
++ addData() to add the next chunk of XML, when the stream is being
++ read from a QByteArray, or by waiting for more data to arrive when
++ the stream is being read from a device().
++
++ \sa tokenType(), tokenString()
++ */
++ QXmlStreamReader::TokenType QXmlStreamReader::readNext()
++ {
++ Q_D(QXmlStreamReader);
++ if (d->type != Invalid) {
++ if (!d->hasCheckedStartDocument)
++ if (!d->checkStartDocument())
++ return d->type; // synthetic StartDocument or error
++ d->parse();
++ if (d->atEnd && d->type != EndDocument && d->type != Invalid)
++ d->raiseError(PrematureEndOfDocumentError);
++ else if (!d->atEnd && d->type == EndDocument)
++ d->raiseWellFormedError(QXmlStream::tr("Extra content at end of document."));
++ } else if (d->error == PrematureEndOfDocumentError) {
++ // resume error
++ d->type = NoToken;
++ d->atEnd = false;
++ d->token = -1;
++ return readNext();
++ }
++ d->checkToken();
++ return d->type;
++ }
++
++
++ /*!
++ Returns the type of the current token.
++
++ The current token can also be queried with the convenience functions
++ isStartDocument(), isEndDocument(), isStartElement(),
++ isEndElement(), isCharacters(), isComment(), isDTD(),
++ isEntityReference(), and isProcessingInstruction().
++
++ \sa tokenString()
++ */
++ QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->type;
++ }
++
++ /*!
++ Reads until the next start element within the current element. Returns \c true
++ when a start element was reached. When the end element was reached, or when
++ an error occurred, false is returned.
++
++ The current element is the element matching the most recently parsed start
++ element of which a matching end element has not yet been reached. When the
++ parser has reached the end element, the current element becomes the parent
++ element.
++
++ This is a convenience function for when you're only concerned with parsing
++ XML elements. The \l{QXmlStream Bookmarks Example} makes extensive use of
++ this function.
++
++ \since 4.6
++ \sa readNext()
++ */
++ bool QXmlStreamReader::readNextStartElement()
++ {
++ while (readNext() != Invalid) {
++ if (isEndElement() || isEndDocument())
++ return false;
++ else if (isStartElement())
++ return true;
++ }
++ return false;
++ }
++
++ /*!
++ Reads until the end of the current element, skipping any child nodes.
++ This function is useful for skipping unknown elements.
++
++ The current element is the element matching the most recently parsed start
++ element of which a matching end element has not yet been reached. When the
++ parser has reached the end element, the current element becomes the parent
++ element.
++
++ \since 4.6
++ */
++ void QXmlStreamReader::skipCurrentElement()
++ {
++ int depth = 1;
++ while (depth && readNext() != Invalid) {
++ if (isEndElement())
++ --depth;
++ else if (isStartElement())
++ ++depth;
++ }
++ }
++
++ /*!
++ Reads and returns the raw inner XML content of the current element.
++ This function is useful for retrieving the full contents embedded inside
++ an element, including nested tags, text, comments, processing instructions,
++ CDATA sections, and other markup — preserving the original XML structure.
++
++ The current element is the element matching the most recently parsed start
++ element of which a matching end element has not yet been reached. When the
++ parser has reached the end element, the current element becomes the parent
++ element.
++
++ \note Entity references defined in the DTD are resolved during parsing
++ and returned as plain text, since DTD declarations are processed
++ separately and are not part of the element’s content.
++ Only the five predefined XML entities (\c <, \c >, \c &,
++ \c ', \c ") are re-escaped in the output.
++
++ \since 6.10
++ */
++ QString QXmlStreamReader::readRawInnerData()
++ {
++ Q_D(QXmlStreamReader);
++ QString raw;
++
++ auto specialToEntities = [](QStringView text, QString &output) {
++ qsizetype chunk = 0;
++ QLatin1StringView replacement;
++ const qsizetype sz = text.size();
++ for (qsizetype i = 0; i < sz; ++i) {
++ switch (text[i].unicode()) {
++ case '<':
++ replacement = "<"_L1;
++ break;
++ case '>':
++ replacement = ">"_L1;
++ break;
++ case '&':
++ replacement = "&"_L1;
++ break;
++ case '"':
++ replacement = """_L1;
++ break;
++ case '\'':
++ replacement = "'"_L1;
++ break;
++ default:
++ continue;
++ }
++ if (chunk < i)
++ output += text.mid(chunk, i - chunk);
++ output += replacement;
++ chunk = i + 1;
++ }
++ if (chunk < text.size())
++ output += text.mid(chunk);
++ };
++
++ if (isStartElement()) {
++ int depth = 1;
++ while (!atEnd() && depth) {
++ switch (readNext()) {
++ case StartElement: {
++ raw += '<'_L1 + name();
++ const QXmlStreamAttributes attrs = attributes();
++ for (auto it = attrs.begin(); it != attrs.end(); ++it) {
++ raw += ' '_L1 + it->name() + "=\""_L1;
++ specialToEntities(it->value(), raw);
++ raw += '"'_L1;
++ }
++ raw += '>'_L1;
++ ++depth;
++ break;
++ }
++ case EndElement:
++ --depth;
++ if (depth > 0)
++ raw += "</"_L1 + name() + '>'_L1;
++ break;
++ case Characters:
++ if (isCDATA())
++ raw += "<![CDATA["_L1 + text() + "]]>"_L1;
++ else
++ specialToEntities(text(), raw);
++ break;
++ case Comment:
++ raw += "<!--"_L1 + text() + "-->"_L1;
++ break;
++ case EntityReference:
++ raw += '&'_L1 + name() + ';'_L1;
++ break;
++ case ProcessingInstruction:
++ raw += "<?"_L1 + processingInstructionTarget()
++ + ' '_L1 + processingInstructionData()
++ + "?>"_L1;
++ break;
++ Q_FALLTHROUGH();
++ default:
++ if (!hasError()) {
++ d->raiseError(NotWellFormedError,
++ QXmlStream::tr("Unexpected token while "
++ "reading raw inner data."));
++ }
++ return raw;
++ }
++ }
++ }
++ return raw;
++ }
++
++ static constexpr auto QXmlStreamReader_tokenTypeString = qOffsetStringArray(
++ "NoToken",
++ "Invalid",
++ "StartDocument",
++ "EndDocument",
++ "StartElement",
++ "EndElement",
++ "Characters",
++ "Comment",
++ "DTD",
++ "EntityReference",
++ "ProcessingInstruction"
++ );
++
++ static constexpr auto QXmlStreamReader_XmlContextString = qOffsetStringArray(
++ "Prolog",
++ "Body"
++ );
++
++ /*!
++ \property QXmlStreamReader::namespaceProcessing
++ \brief the namespace-processing flag of the stream reader.
++
++ This property controls whether or not the stream reader processes
++ namespaces. If enabled, the reader processes namespaces, otherwise
++ it does not.
++
++ By default, namespace-processing is enabled.
++ */
++
++
++ void QXmlStreamReader::setNamespaceProcessing(bool enable)
++ {
++ Q_D(QXmlStreamReader);
++ d->namespaceProcessing = enable;
++ }
++
++ bool QXmlStreamReader::namespaceProcessing() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->namespaceProcessing;
++ }
++
++ /*! Returns the reader's current token as string.
++
++ \sa tokenType()
++ */
++ QString QXmlStreamReader::tokenString() const
++ {
++ Q_D(const QXmlStreamReader);
++ return QLatin1StringView(QXmlStreamReader_tokenTypeString.at(d->type));
++ }
++
++ /*!
++ \internal
++ \return \param ctxt (Prolog/Body) as a string.
++ */
++ static constexpr QLatin1StringView contextString(QXmlStreamReaderPrivate::XmlContext ctxt)
++ {
++ return QLatin1StringView(QXmlStreamReader_XmlContextString.viewAt(static_cast<int>(ctxt)));
++ }
++
++ #endif // feature xmlstreamreader
++
++ QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack()
++ {
++ tagStack.reserve(16);
++ tagStackStringStorageSize = 0;
++ NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
++ namespaceDeclaration.namespaceUri = addToStringStorage(u"http://www.w3.org/XML/1998/namespace");
++ namespaceDeclaration.prefix = addToStringStorage(u"xml");
++ initialTagStackStringStorageSize = tagStackStringStorageSize;
++ tagsDone = false;
++ }
++
++ #if QT_CONFIG(xmlstreamreader)
++
++ QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q)
++ :q_ptr(q)
++ {
++ device = nullptr;
++ deleteDevice = false;
++ stack_size = 64;
++ sym_stack = nullptr;
++ state_stack = nullptr;
++ reallocateStack();
++ entityResolver = nullptr;
++ init();
++ }
++
++ void QXmlStreamReaderPrivate::init()
++ {
++ entityHash.clear();
++ #define ADD_PREDEFINED(n, v) \
++ do { \
++ Entity e = Entity::createLiteral(n##_L1, v##_L1); \
++ entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \
++ } while (false)
++ ADD_PREDEFINED("lt", "<");
++ ADD_PREDEFINED("gt", ">");
++ ADD_PREDEFINED("amp", "&");
++ ADD_PREDEFINED("apos", "'");
++ ADD_PREDEFINED("quot", "\"");
++ #undef ADD_PREDEFINED
++ parameterEntityHash.clear();
++ scanDtd = false;
++ lastAttributeIsCData = false;
++ token = -1;
++ token_char = 0;
++ isEmptyElement = false;
++ isWhitespace = true;
++ isCDATA = false;
++ standalone = false;
++ hasStandalone = false;
++ tos = 0;
++ resumeReduction = 0;
++ state_stack[tos++] = 0;
++ state_stack[tos] = 0;
++ putStack.clear();
++ putStack.reserve(32);
++ dtdName.clear();
++ dtdPublicId.clear();
++ dtdSystemId.clear();
++ entityDeclarations.clear();
++ entityDeclarations.reserve(16);
++ publicEntityDeclarations.clear();
++ notationDeclarations.clear();
++ notationDeclarations.reserve(16);
++ publicNotationDeclarations.clear();
++ textBuffer.clear();
++ textBuffer.reserve(256);
++ tagStack.clear();
++ tagsDone = false;
++ attributes.clear();
++ attributes.reserve(16);
++ lineNumber = lastLineStart = characterOffset = 0;
++ readBufferPos = 0;
++ nbytesread = 0;
++ decoder = QStringDecoder();
++ attributeStack.clear();
++ attributeStack.reserve(16);
++ entityReferenceStack.clear();
++ entityReferenceStack.reserve(16);
++ entityLength = 0;
++ entityParser.reset();
++ hasCheckedStartDocument = false;
++ normalizeLiterals = false;
++ hasSeenTag = false;
++ atEnd = false;
++ inParseEntity = false;
++ referenceToUnparsedEntityDetected = false;
++ referenceToParameterEntityDetected = false;
++ hasExternalDtdSubset = false;
++ lockEncoding = false;
++ namespaceProcessing = true;
++ rawReadBuffer.clear();
++ chunkDecoder = QStringDecoder();
++ dataInfo.clear();
++ readBuffer.clear();
++ tagStackStringStorageSize = initialTagStackStringStorageSize;
++
++ type = QXmlStreamReader::NoToken;
++ error = QXmlStreamReader::NoError;
++ currentContext = XmlContext::Prolog;
++ foundDTD = false;
++ }
++
++ /*
++ Well-formed requires that we verify entity values. We do this with a
++ standard parser.
++ */
++ void QXmlStreamReaderPrivate::parseEntity(const QString &value)
++ {
++ Q_Q(QXmlStreamReader);
++
++ if (value.isEmpty())
++ return;
++
++
++ if (!entityParser)
++ entityParser = std::make_unique<QXmlStreamReaderPrivate>(q);
++ else
++ entityParser->init();
++ entityParser->inParseEntity = true;
++ entityParser->readBuffer = value;
++ entityParser->injectToken(PARSE_ENTITY);
++ while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid)
++ entityParser->parse();
++ if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size())
++ raiseWellFormedError(QXmlStream::tr("Invalid entity value."));
++
++ }
++
++ inline void QXmlStreamReaderPrivate::reallocateStack()
++ {
++ // The multiplication cannot overflow (we would have alloc'ed more than
++ // half of the address space using the realloc()s below, before that
++ // happens):
++ const auto newStackSize = stack_size * 2;
++
++ void *p = qt_reallocarray(sym_stack, size_t(newStackSize), sizeof(Value));
++ Q_CHECK_PTR(p);
++ sym_stack = static_cast<Value*>(p);
++ p = qt_reallocarray(state_stack, size_t(newStackSize), sizeof(int));
++ Q_CHECK_PTR(p);
++ state_stack = static_cast<int*>(p);
++
++ // only update this after _both_ allocations succeeded:
++ stack_size = newStackSize;
++ }
++
++
++ QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate()
++ {
++ free(sym_stack);
++ free(state_stack);
++ }
++
++
++ inline uint QXmlStreamReaderPrivate::filterCarriageReturn()
++ {
++ uint peekc = peekChar();
++ if (peekc == '\n') {
++ if (putStack.size())
++ putStack.pop();
++ else
++ ++readBufferPos;
++ return peekc;
++ }
++ if (peekc == StreamEOF) {
++ putChar('\r');
++ return 0;
++ }
++ return '\n';
++ }
++
++ /*!
++ \internal
++ If the end of the file is encountered, ~0 is returned.
++ */
++ inline uint QXmlStreamReaderPrivate::getChar()
++ {
++ uint c;
++ if (putStack.size()) {
++ c = atEnd ? StreamEOF : putStack.pop();
++ } else {
++ if (readBufferPos < readBuffer.size())
++ c = readBuffer.at(readBufferPos++).unicode();
++ else
++ c = getChar_helper();
++ }
++
++ return c;
++ }
++
++ inline uint QXmlStreamReaderPrivate::peekChar()
++ {
++ uint c;
++ if (putStack.size()) {
++ c = putStack.top();
++ } else if (readBufferPos < readBuffer.size()) {
++ c = readBuffer.at(readBufferPos).unicode();
++ } else {
++ if ((c = getChar_helper()) != StreamEOF)
++ --readBufferPos;
++ }
++
++ return c;
++ }
++
++ /*!
++ \internal
++
++ Scans characters until \a str is encountered, and validates the characters
++ as according to the Char[2] production and do the line-ending normalization.
++ If any character is invalid, false is returned, otherwise true upon success.
++
++ If \a tokenToInject is not less than zero, injectToken() is called with
++ \a tokenToInject when \a str is found.
++
++ If any error occurred, false is returned, otherwise true.
++ */
++ bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject)
++ {
++ const qsizetype pos = textBuffer.size();
++ const auto oldLineNumber = lineNumber;
++
++ uint c;
++ while ((c = getChar()) != StreamEOF) {
++ /* First, we do the validation & normalization. */
++ switch (c) {
++ case '\r':
++ if ((c = filterCarriageReturn()) == 0)
++ break;
++ Q_FALLTHROUGH();
++ case '\n':
++ ++lineNumber;
++ lastLineStart = characterOffset + readBufferPos;
++ Q_FALLTHROUGH();
++ case '\t':
++ textBuffer += QChar(c);
++ continue;
++ default:
++ if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) {
++ raiseWellFormedError(QXmlStream::tr("Invalid XML character."));
++ lineNumber = oldLineNumber;
++ return false;
++ }
++ textBuffer += QChar(c);
++ }
++
++
++ /* Second, attempt to lookup str. */
++ if (c == uint(*str)) {
++ if (!*(str + 1)) {
++ if (tokenToInject >= 0)
++ injectToken(tokenToInject);
++ return true;
++ } else {
++ if (scanString(str + 1, tokenToInject, false))
++ return true;
++ }
++ }
++ }
++ putString(textBuffer, pos);
++ textBuffer.resize(pos);
++ lineNumber = oldLineNumber;
++ return false;
++ }
++
++ bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace)
++ {
++ qsizetype n = 0;
++ while (str[n]) {
++ uint c = getChar();
++ if (c != ushort(str[n])) {
++ if (c != StreamEOF)
++ putChar(c);
++ while (n--) {
++ putChar(ushort(str[n]));
++ }
++ return false;
++ }
++ ++n;
++ }
++ textBuffer += QLatin1StringView(str, n);
++ if (requireSpace) {
++ const qsizetype s = fastScanSpace();
++ if (!s || atEnd) {
++ qsizetype pos = textBuffer.size() - n - s;
++ putString(textBuffer, pos);
++ textBuffer.resize(pos);
++ return false;
++ }
++ }
++ if (tokenToInject >= 0)
++ injectToken(tokenToInject);
++ return true;
++ }
++
++ bool QXmlStreamReaderPrivate::scanAfterLangleBang()
++ {
++ switch (peekChar()) {
++ case '[':
++ return scanString(spell[CDATA_START], CDATA_START, false);
++ case 'D':
++ return scanString(spell[DOCTYPE], DOCTYPE);
++ case 'A':
++ return scanString(spell[ATTLIST], ATTLIST);
++ case 'N':
++ return scanString(spell[NOTATION], NOTATION);
++ case 'E':
++ if (scanString(spell[ELEMENT], ELEMENT))
++ return true;
++ return scanString(spell[ENTITY], ENTITY);
++
++ default:
++ ;
++ };
++ return false;
++ }
++
++ bool QXmlStreamReaderPrivate::scanPublicOrSystem()
++ {
++ switch (peekChar()) {
++ case 'S':
++ return scanString(spell[SYSTEM], SYSTEM);
++ case 'P':
++ return scanString(spell[PUBLIC], PUBLIC);
++ default:
++ ;
++ }
++ return false;
++ }
++
++ bool QXmlStreamReaderPrivate::scanNData()
++ {
++ if (fastScanSpace()) {
++ if (scanString(spell[NDATA], NDATA))
++ return true;
++ putChar(' ');
++ }
++ return false;
++ }
++
++ bool QXmlStreamReaderPrivate::scanAfterDefaultDecl()
++ {
++ switch (peekChar()) {
++ case 'R':
++ return scanString(spell[REQUIRED], REQUIRED, false);
++ case 'I':
++ return scanString(spell[IMPLIED], IMPLIED, false);
++ case 'F':
++ return scanString(spell[FIXED], FIXED, false);
++ default:
++ ;
++ }
++ return false;
++ }
++
++ bool QXmlStreamReaderPrivate::scanAttType()
++ {
++ switch (peekChar()) {
++ case 'C':
++ return scanString(spell[CDATA], CDATA);
++ case 'I':
++ if (scanString(spell[ID], ID))
++ return true;
++ if (scanString(spell[IDREF], IDREF))
++ return true;
++ return scanString(spell[IDREFS], IDREFS);
++ case 'E':
++ if (scanString(spell[ENTITY], ENTITY))
++ return true;
++ return scanString(spell[ENTITIES], ENTITIES);
++ case 'N':
++ if (scanString(spell[NOTATION], NOTATION))
++ return true;
++ if (scanString(spell[NMTOKEN], NMTOKEN))
++ return true;
++ return scanString(spell[NMTOKENS], NMTOKENS);
++ default:
++ ;
++ }
++ return false;
++ }
++
++ /*!
++ \internal
++
++ Scan strings with quotes or apostrophes surround them. For instance,
++ attributes, the version and encoding field in the XML prolog and
++ entity declarations.
++
++ If normalizeLiterals is set to true, the function also normalizes
++ whitespace. It is set to true when the first start tag is
++ encountered.
++
++ */
++ inline qsizetype QXmlStreamReaderPrivate::fastScanLiteralContent()
++ {
++ qsizetype n = 0;
++ uint c;
++ while ((c = getChar()) != StreamEOF) {
++ switch (ushort(c)) {
++ case 0xfffe:
++ case 0xffff:
++ case 0:
++ /* The putChar() call is necessary so the parser re-gets
++ * the character from the input source, when raising an error. */
++ putChar(c);
++ return n;
++ case '\r':
++ if (filterCarriageReturn() == 0)
++ return n;
++ Q_FALLTHROUGH();
++ case '\n':
++ ++lineNumber;
++ lastLineStart = characterOffset + readBufferPos;
++ Q_FALLTHROUGH();
++ case ' ':
++ case '\t':
++ if (normalizeLiterals)
++ textBuffer += u' ';
++ else
++ textBuffer += QChar(c);
++ ++n;
++ break;
++ case '&':
++ case '<':
++ case '\"':
++ case '\'':
++ if (!(c & 0xff0000)) {
++ putChar(c);
++ return n;
++ }
++ Q_FALLTHROUGH();
++ default:
++ if (c < 0x20) {
++ putChar(c);
++ return n;
++ }
++ textBuffer += QChar(ushort(c));
++ ++n;
++ }
++ }
++ return n;
++ }
++
++ inline qsizetype QXmlStreamReaderPrivate::fastScanSpace()
++ {
++ qsizetype n = 0;
++ uint c;
++ while ((c = getChar()) != StreamEOF) {
++ switch (c) {
++ case '\r':
++ if ((c = filterCarriageReturn()) == 0)
++ return n;
++ Q_FALLTHROUGH();
++ case '\n':
++ ++lineNumber;
++ lastLineStart = characterOffset + readBufferPos;
++ Q_FALLTHROUGH();
++ case ' ':
++ case '\t':
++ textBuffer += QChar(c);
++ ++n;
++ break;
++ default:
++ putChar(c);
++ return n;
++ }
++ }
++ return n;
++ }
++
++ /*!
++ \internal
++
++ Used for text nodes essentially. That is, characters appearing
++ inside elements.
++ */
++ inline qsizetype QXmlStreamReaderPrivate::fastScanContentCharList()
++ {
++ qsizetype n = 0;
++ uint c;
++ while ((c = getChar()) != StreamEOF) {
++ switch (ushort(c)) {
++ case 0xfffe:
++ case 0xffff:
++ case 0:
++ putChar(c);
++ return n;
++ case ']': {
++ isWhitespace = false;
++ const qsizetype pos = textBuffer.size();
++ textBuffer += QChar(ushort(c));
++ ++n;
++ while ((c = getChar()) == ']') {
++ textBuffer += QChar(ushort(c));
++ ++n;
++ }
++ if (c == StreamEOF) {
++ putString(textBuffer, pos);
++ textBuffer.resize(pos);
++ } else if (c == '>' && textBuffer.at(textBuffer.size() - 2) == u']') {
++ raiseWellFormedError(QXmlStream::tr("Sequence ']]>' not allowed in content."));
++ } else {
++ putChar(c);
++ break;
++ }
++ return n;
++ } break;
++ case '\r':
++ if ((c = filterCarriageReturn()) == 0)
++ return n;
++ Q_FALLTHROUGH();
++ case '\n':
++ ++lineNumber;
++ lastLineStart = characterOffset + readBufferPos;
++ Q_FALLTHROUGH();
++ case ' ':
++ case '\t':
++ textBuffer += QChar(ushort(c));
++ ++n;
++ break;
++ case '&':
++ case '<':
++ if (!(c & 0xff0000)) {
++ putChar(c);
++ return n;
++ }
++ Q_FALLTHROUGH();
++ default:
++ if (c < 0x20) {
++ putChar(c);
++ return n;
++ }
++ isWhitespace = false;
++ textBuffer += QChar(ushort(c));
++ ++n;
++ }
++ }
++ return n;
++ }
++
++ // Fast scan an XML attribute name (e.g. "xml:lang").
++ inline std::optional<qsizetype> QXmlStreamReaderPrivate::fastScanName(Value *val)
++ {
++ qsizetype n = 0;
++ uint c;
++ while ((c = getChar()) != StreamEOF) {
++ if (n >= 4096) {
++ // This is too long to be a sensible name, and
++ // can exhaust memory, or the range of decltype(*prefix)
++ raiseNamePrefixTooLongError();
++ return std::nullopt;
++ }
++ switch (c) {
++ case '\n':
++ case ' ':
++ case '\t':
++ case '\r':
++ case '&':
++ case '#':
++ case '\'':
++ case '\"':
++ case '<':
++ case '>':
++ case '[':
++ case ']':
++ case '=':
++ case '%':
++ case '/':
++ case ';':
++ case '?':
++ case '!':
++ case '^':
++ case '|':
++ case ',':
++ case '(':
++ case ')':
++ case '+':
++ case '*':
++ putChar(c);
++ if (val && val->prefix == n + 1) {
++ val->prefix = 0;
++ putChar(':');
++ --n;
++ }
++ return n;
++ case ':':
++ if (val) {
++ if (val->prefix == 0) {
++ val->prefix = qint16(n + 2);
++ } else { // only one colon allowed according to the namespace spec.
++ putChar(c);
++ return n;
++ }
++ } else {
++ putChar(c);
++ return n;
++ }
++ Q_FALLTHROUGH();
++ default:
++ textBuffer += QChar(ushort(c));
++ ++n;
++ }
++ }
++
++ if (val)
++ val->prefix = 0;
++ qsizetype pos = textBuffer.size() - n;
++ putString(textBuffer, pos);
++ textBuffer.resize(pos);
++ return 0;
++ }
++
++ enum NameChar { NameBeginning, NameNotBeginning, NotName };
++
++ static const char Begi = static_cast<char>(NameBeginning);
++ static const char NtBg = static_cast<char>(NameNotBeginning);
++ static const char NotN = static_cast<char>(NotName);
++
++ static const char nameCharTable[128] =
++ {
++ // 0x00
++ NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
++ NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
++ // 0x10
++ NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
++ NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
++ // 0x20 (0x2D is '-', 0x2E is '.')
++ NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
++ NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN,
++ // 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':')
++ NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg,
++ NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN,
++ // 0x40 (0x41..0x5A are 'A'..'Z')
++ NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
++ Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
++ // 0x50 (0x5F is '_')
++ Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
++ Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi,
++ // 0x60 (0x61..0x7A are 'a'..'z')
++ NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
++ Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
++ // 0x70
++ Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
++ Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN
++ };
++
++ static inline NameChar fastDetermineNameChar(QChar ch)
++ {
++ ushort uc = ch.unicode();
++ if (!(uc & ~0x7f)) // uc < 128
++ return static_cast<NameChar>(nameCharTable[uc]);
++
++ QChar::Category cat = ch.category();
++ // ### some these categories might be slightly wrong
++ if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other)
++ || cat == QChar::Number_Letter)
++ return NameBeginning;
++ if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other)
++ || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing))
++ return NameNotBeginning;
++ return NotName;
++ }
++
++ inline qsizetype QXmlStreamReaderPrivate::fastScanNMTOKEN()
++ {
++ qsizetype n = 0;
++ uint c;
++ while ((c = getChar()) != StreamEOF) {
++ if (fastDetermineNameChar(QChar(c)) == NotName) {
++ putChar(c);
++ return n;
++ } else {
++ ++n;
++ textBuffer += QChar(c);
++ }
++ }
++
++ qsizetype pos = textBuffer.size() - n;
++ putString(textBuffer, pos);
++ textBuffer.resize(pos);
++
++ return n;
++ }
++
++ void QXmlStreamReaderPrivate::putString(QStringView s, qsizetype from)
++ {
++ if (from != 0) {
++ putString(s.mid(from));
++ return;
++ }
++ putStack.reserve(s.size());
++ for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
++ putStack.rawPush() = it->unicode();
++ }
++
++ void QXmlStreamReaderPrivate::putStringLiteral(QStringView s)
++ {
++ putStack.reserve(s.size());
++ for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
++ putStack.rawPush() = ((LETTER << 16) | it->unicode());
++ }
++
++ void QXmlStreamReaderPrivate::putReplacement(QStringView s)
++ {
++ putStack.reserve(s.size());
++ for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
++ char16_t c = it->unicode();
++ if (c == '\n' || c == '\r')
++ putStack.rawPush() = ((LETTER << 16) | c);
++ else
++ putStack.rawPush() = c;
++ }
++ }
++ void QXmlStreamReaderPrivate::putReplacementInAttributeValue(QStringView s)
++ {
++ putStack.reserve(s.size());
++ for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
++ char16_t c = it->unicode();
++ if (c == '&' || c == ';')
++ putStack.rawPush() = c;
++ else if (c == '\n' || c == '\r')
++ putStack.rawPush() = ' ';
++ else
++ putStack.rawPush() = ((LETTER << 16) | c);
++ }
++ }
++
++ uint QXmlStreamReaderPrivate::getChar_helper()
++ {
++ constexpr qsizetype BUFFER_SIZE = 8192;
++ characterOffset += readBufferPos;
++ readBufferPos = 0;
++ if (readBuffer.size())
++ readBuffer.resize(0);
++ if (decoder.isValid())
++ nbytesread = 0;
++
++ auto tryDecodeWithGlobalDecoder = [this]() -> bool {
++ if (!decoder.isValid()) {
++ // Need 4 bytes: three for BOM (EF BB BF) plus one for the UTF-8 codec
++ if (nbytesread < 4) {
++ atEnd = true;
++ return false;
++ }
++ auto encoding = QStringDecoder::encodingForData(rawReadBuffer, u'<');
++ if (!encoding) // assume utf-8
++ encoding = QStringDecoder::Utf8;
++ decoder = QStringDecoder(*encoding);
++ }
++
++ readBuffer = decoder(QByteArrayView(rawReadBuffer).first(nbytesread));
++
++ if (lockEncoding && decoder.hasError()) {
++ readBuffer.clear();
++ return false;
++ }
++
++ return true;
++ };
++
++ if (device) {
++ rawReadBuffer.resize(BUFFER_SIZE);
++ qint64 nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread);
++ nbytesread += qMax(nbytesreadOrMinus1, qint64{0});
++
++ if (!nbytesread) {
++ atEnd = true;
++ return StreamEOF;
++ }
++
++ if (!tryDecodeWithGlobalDecoder())
++ return StreamEOF;
++ } else if (dataInfo.empty()) {
++ atEnd = true;
++ return StreamEOF;
++ } else {
++ const BufferAndEncoding bufAndEnc = dataInfo.takeFirst();
++
++ // Use global decoder if the encoding is not set explicitly.
++ // Here we'll use rawReadBuffer to cache the data from the previous
++ // chunk with unknown encoding. We need to do it because the size
++ // of the previous chunk might be too small, and we need to wait
++ // for more data before we can determine the encoding.
++ if (bufAndEnc.encoding == QStringDecoder::System) {
++ if (nbytesread)
++ rawReadBuffer += bufAndEnc.buffer;
++ else
++ rawReadBuffer = bufAndEnc.buffer;
++ nbytesread = rawReadBuffer.size();
++
++ if (!tryDecodeWithGlobalDecoder()) {
++ // try decoding with the previous chunk decoder
++ bool hasError = true;
++ if (chunkDecoder.isValid() && !chunkDecoder.hasError()) {
++ readBuffer = chunkDecoder(QByteArrayView(rawReadBuffer).first(nbytesread));
++ hasError = chunkDecoder.hasError();
++ }
++ if (hasError) {
++ raiseWellFormedError(
++ QXmlStream::tr("Encountered incorrectly encoded content."));
++ return StreamEOF;
++ }
++ }
++ } else {
++ if (!isDecoderForEncoding(chunkDecoder, bufAndEnc.encoding))
++ chunkDecoder = QStringDecoder(bufAndEnc.encoding);
++ readBuffer = chunkDecoder(bufAndEnc.buffer);
++ }
++ }
++
++ readBuffer.reserve(1); // keep capacity when calling resize() next time
++
++ if (readBufferPos < readBuffer.size()) {
++ ushort c = readBuffer.at(readBufferPos++).unicode();
++ return c;
++ }
++
++ atEnd = true;
++ return StreamEOF;
++ }
++
++ XmlStringRef QXmlStreamReaderPrivate::namespaceForPrefix(QStringView prefix)
++ {
++ for (const NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
++ if (namespaceDeclaration.prefix == prefix) {
++ return namespaceDeclaration.namespaceUri;
++ }
++ }
++
++ #if 1
++ if (namespaceProcessing && !prefix.isEmpty())
++ raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix));
++ #endif
++
++ return XmlStringRef();
++ }
++
++ struct AttributeName
++ {
++ QStringView name;
++ QStringView namespaceUri;
++
++ static AttributeName fromXmlAttribute(const QXmlStreamAttribute &a, bool nsProcessing)
++ {
++ if (nsProcessing)
++ return {a.name(), a.namespaceUri()};
++ else
++ return {a.qualifiedName(), a.namespaceUri()};
++ }
++
++ friend bool operator==(const AttributeName &lhs, const AttributeName &rhs) noexcept
++ {
++ return lhs.name == rhs.name
++ && lhs.namespaceUri == rhs.namespaceUri;
++ }
++ friend size_t qHash(const AttributeName &key, size_t seed = 0) noexcept
++ {
++ return qHashMulti(seed,
++ key.name,
++ key.namespaceUri);
++ }
++ };
++
++ /*
++ uses namespaceForPrefix and builds the attribute vector
++ */
++ void QXmlStreamReaderPrivate::resolveTag()
++ {
++ const auto attributeStackCleaner = qScopeGuard([this](){ attributeStack.clear(); });
++ const qsizetype n = attributeStack.size();
++
++ if (namespaceProcessing) {
++ for (const DtdAttribute &dtdAttribute : dtdAttributes) {
++ if (!dtdAttribute.isNamespaceAttribute
++ || dtdAttribute.defaultValue.isNull()
++ || dtdAttribute.tagName != qualifiedName
++ || dtdAttribute.attributeQualifiedName.isNull())
++ continue;
++ qsizetype i = 0;
++ while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
++ ++i;
++ if (i != n)
++ continue;
++ if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == "xmlns"_L1) {
++ NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
++ namespaceDeclaration.prefix.clear();
++
++ const XmlStringRef ns(dtdAttribute.defaultValue);
++ if (ns == "http://www.w3.org/2000/xmlns/"_L1 ||
++ ns == "http://www.w3.org/XML/1998/namespace"_L1)
++ raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
++ else
++ namespaceDeclaration.namespaceUri = ns;
++ } else if (dtdAttribute.attributePrefix == "xmlns"_L1) {
++ NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
++ XmlStringRef namespacePrefix = dtdAttribute.attributeName;
++ XmlStringRef namespaceUri = dtdAttribute.defaultValue;
++ if (((namespacePrefix == "xml"_L1)
++ ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1))
++ || namespaceUri == "http://www.w3.org/2000/xmlns/"_L1
++ || namespaceUri.isEmpty()
++ || namespacePrefix == "xmlns"_L1)
++ raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
++
++ namespaceDeclaration.prefix = namespacePrefix;
++ namespaceDeclaration.namespaceUri = namespaceUri;
++ }
++ }
++ }
++
++ tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix);
++
++ attributes.resize(n);
++
++ Q_DECL_UNINITIALIZED
++ QDuplicateTracker<AttributeName, 13> names(n);
++
++ for (qsizetype i = 0; i < n; ++i) {
++ QXmlStreamAttribute &attribute = attributes[i];
++ Attribute &attrib = attributeStack[i];
++ XmlStringRef prefix(symPrefix(attrib.key));
++ XmlStringRef name(symString(attrib.key));
++ XmlStringRef qualifiedName(symName(attrib.key));
++ XmlStringRef value(symString(attrib.value));
++
++ attribute.m_name = name;
++ attribute.m_qualifiedName = qualifiedName;
++ attribute.m_value = value;
++
++ if (!prefix.isEmpty()) {
++ XmlStringRef attributeNamespaceUri = namespaceForPrefix(prefix);
++ attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
++ }
++
++ if (names.hasSeen(AttributeName::fromXmlAttribute(attribute, namespaceProcessing))) {
++ raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName()));
++ return;
++ }
++ }
++
++ for (const DtdAttribute &dtdAttribute : dtdAttributes) {
++ if (dtdAttribute.isNamespaceAttribute
++ || dtdAttribute.defaultValue.isNull()
++ || dtdAttribute.tagName != qualifiedName
++ || dtdAttribute.attributeQualifiedName.isNull())
++ continue;
++ qsizetype i = 0;
++ while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
++ ++i;
++ if (i != n)
++ continue;
++
++
++
++ QXmlStreamAttribute attribute;
++ attribute.m_name = dtdAttribute.attributeName;
++ attribute.m_qualifiedName = dtdAttribute.attributeQualifiedName;
++ attribute.m_value = dtdAttribute.defaultValue;
++
++ if (!dtdAttribute.attributePrefix.isEmpty()) {
++ XmlStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix);
++ attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
++ }
++
++ // Check that the DTD doesn't complement the element's ns1:a with a
++ // ns2:a where the ns1 and ns2 prefixes resolve to the same
++ // namespace-URI. This can only happen when namespaceProcessing is on,
++ // otherwise the prefixes would have matched, and the DTD attribute skipped,
++ // in the loop over `i` above.
++
++ if (namespaceProcessing && names.hasSeen(AttributeName::fromXmlAttribute(attribute, true))) {
++ raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName()));
++ return;
++ }
++
++ attribute.m_isDefault = true;
++ attributes.append(std::move(attribute));
++ }
++ }
++
++ void QXmlStreamReaderPrivate::resolvePublicNamespaces()
++ {
++ const Tag &tag = tagStack.top();
++ qsizetype n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize;
++ publicNamespaceDeclarations.resize(n);
++ for (qsizetype i = 0; i < n; ++i) {
++ const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i);
++ QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i];
++ publicNamespaceDeclaration.m_prefix = namespaceDeclaration.prefix;
++ publicNamespaceDeclaration.m_namespaceUri = namespaceDeclaration.namespaceUri;
++ }
++ }
++
++ void QXmlStreamReaderPrivate::resolveDtd()
++ {
++ publicNotationDeclarations.resize(notationDeclarations.size());
++ for (qsizetype i = 0; i < notationDeclarations.size(); ++i) {
++ const QXmlStreamReaderPrivate::NotationDeclaration ¬ationDeclaration = notationDeclarations.at(i);
++ QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i];
++ publicNotationDeclaration.m_name = notationDeclaration.name;
++ publicNotationDeclaration.m_systemId = notationDeclaration.systemId;
++ publicNotationDeclaration.m_publicId = notationDeclaration.publicId;
++
++ }
++ notationDeclarations.clear();
++ publicEntityDeclarations.resize(entityDeclarations.size());
++ for (qsizetype i = 0; i < entityDeclarations.size(); ++i) {
++ const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i);
++ QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i];
++ publicEntityDeclaration.m_name = entityDeclaration.name;
++ publicEntityDeclaration.m_notationName = entityDeclaration.notationName;
++ publicEntityDeclaration.m_systemId = entityDeclaration.systemId;
++ publicEntityDeclaration.m_publicId = entityDeclaration.publicId;
++ publicEntityDeclaration.m_value = entityDeclaration.value;
++ }
++ entityDeclarations.clear();
++ parameterEntityHash.clear();
++ }
++
++ uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex)
++ {
++ bool ok = true;
++ uint s;
++ // ### add toXShort to XmlString?
++ if (sym(symbolIndex).c == 'x')
++ s = symString(symbolIndex).view().sliced(1).toUInt(&ok, 16);
++ else
++ s = symString(symbolIndex).view().toUInt(&ok, 10);
++
++ ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff)
++ || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint));
++
++ return ok ? s : 0;
++ }
++
++
++ void QXmlStreamReaderPrivate::checkPublicLiteral(QStringView publicId)
++ {
++ //#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
++
++ const char16_t *data = publicId.utf16();
++ uchar c = 0;
++ qsizetype i;
++ for (i = publicId.size() - 1; i >= 0; --i) {
++ if (data[i] < 256)
++ switch ((c = data[i])) {
++ case ' ': case '\n': case '\r': case '-': case '(': case ')':
++ case '+': case ',': case '.': case '/': case ':': case '=':
++ case '?': case ';': case '!': case '*': case '#': case '@':
++ case '$': case '_': case '%': case '\'': case '\"':
++ continue;
++ default:
++ if (isAsciiLetterOrNumber(c))
++ continue;
++ }
++ break;
++ }
++ if (i >= 0)
++ raiseWellFormedError(QXmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c))));
++ }
++
++ /*
++ Checks whether the document starts with an xml declaration. If it
++ does, this function returns \c true; otherwise it sets up everything
++ for a synthetic start document event and returns \c false.
++ */
++ bool QXmlStreamReaderPrivate::checkStartDocument()
++ {
++ hasCheckedStartDocument = true;
++
++ if (scanString(spell[XML], XML))
++ return true;
++
++ type = QXmlStreamReader::StartDocument;
++ if (atEnd) {
++ hasCheckedStartDocument = false;
++ raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
++ }
++ return false;
++ }
++
++ void QXmlStreamReaderPrivate::startDocument()
++ {
++ QString err;
++ if (documentVersion != "1.0"_L1) {
++ if (documentVersion.view().contains(u' '))
++ err = QXmlStream::tr("Invalid XML version string.");
++ else
++ err = QXmlStream::tr("Unsupported XML version.");
++ }
++ qsizetype n = attributeStack.size();
++
++ /* We use this bool to ensure that the pesudo attributes are in the
++ * proper order:
++ *
++ * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
++
++ for (qsizetype i = 0; err.isNull() && i < n; ++i) {
++ Attribute &attrib = attributeStack[i];
++ XmlStringRef prefix(symPrefix(attrib.key));
++ XmlStringRef key(symString(attrib.key));
++ XmlStringRef value(symString(attrib.value));
++
++ if (prefix.isEmpty() && key == "encoding"_L1) {
++ documentEncoding = value;
++
++ if (hasStandalone)
++ err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding.");
++ if (!QXmlUtils::isEncName(value))
++ err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value);
++ else {
++ QByteArray enc = value.toString().toUtf8();
++ if (!lockEncoding) {
++ decoder = QStringDecoder(enc.constData());
++ if (!decoder.isValid()) {
++ // Raise an error only if the data was not already processed
++ // by the chunk decoder. Otherwise simply fall back to
++ // UTF-8 for backwards compatibility
++ if (!chunkDecoder.isValid() || chunkDecoder.hasError())
++ err = QXmlStream::tr("Encoding %1 is unsupported").arg(value);
++ else
++ decoder = QStringDecoder(QStringDecoder::Utf8);
++ } else if (!rawReadBuffer.isEmpty() && nbytesread) {
++ // Try to decode with the newly-determined encoding.
++ // If the decoding is successful, consider it as a
++ // better match for the decoded data.
++ // That is only applicable if the previous chunk had
++ // unspecified (i.e. System) encoding.
++ QString buf = decoder(QByteArrayView(rawReadBuffer).first(nbytesread));
++ if (!decoder.hasError())
++ readBuffer = std::move(buf);
++ }
++ }
++ }
++ } else if (prefix.isEmpty() && key == "standalone"_L1) {
++ hasStandalone = true;
++ if (value == "yes"_L1)
++ standalone = true;
++ else if (value == "no"_L1)
++ standalone = false;
++ else
++ err = QXmlStream::tr("Standalone accepts only yes or no.");
++ } else {
++ err = QXmlStream::tr("Invalid attribute in XML declaration: %1 = %2").arg(key).arg(value);
++ }
++ }
++
++ if (!err.isNull())
++ raiseWellFormedError(err);
++ attributeStack.clear();
++ }
++
++
++ void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message)
++ {
++ this->error = error;
++ errorString = message;
++ if (errorString.isNull()) {
++ if (error == QXmlStreamReader::PrematureEndOfDocumentError)
++ errorString = QXmlStream::tr("Premature end of document.");
++ else if (error == QXmlStreamReader::CustomError)
++ errorString = QXmlStream::tr("Invalid document.");
++ }
++
++ type = QXmlStreamReader::Invalid;
++ }
++
++ void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message)
++ {
++ raiseError(QXmlStreamReader::NotWellFormedError, message);
++ }
++
++ void QXmlStreamReaderPrivate::raiseNamePrefixTooLongError()
++ {
++ // TODO: add a ImplementationLimitsExceededError and use it instead
++ raiseError(QXmlStreamReader::NotWellFormedError,
++ QXmlStream::tr("Length of XML attribute name exceeds implementation limits (4KiB "
++ "characters)."));
++ }
++
++ void QXmlStreamReaderPrivate::parseError()
++ {
++
++ if (token == EOF_SYMBOL) {
++ raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
++ return;
++ }
++ const int nmax = 4;
++ QString error_message;
++ int ers = state_stack[tos];
++ int nexpected = 0;
++ int expected[nmax];
++ if (token != XML_ERROR)
++ for (int tk = 0; tk < TERMINAL_COUNT; ++tk) {
++ int k = t_action(ers, tk);
++ if (k <= 0)
++ continue;
++ if (spell[tk]) {
++ if (nexpected < nmax)
++ expected[nexpected++] = tk;
++ }
++ }
++
++ if (nexpected && nexpected < nmax) {
++ //: '<first option>'
++ QString exp_str = QXmlStream::tr("'%1'", "expected")
++ .arg(QLatin1StringView(spell[expected[0]]));
++ if (nexpected == 2) {
++ //: <first option>, '<second option>'
++ exp_str = QXmlStream::tr("%1 or '%2'", "expected")
++ .arg(exp_str, QLatin1StringView(spell[expected[1]]));
++ } else if (nexpected > 2) {
++ int s = 1;
++ for (; s < nexpected - 1; ++s) {
++ //: <options so far>, '<next option>'
++ exp_str = QXmlStream::tr("%1, '%2'", "expected")
++ .arg(exp_str, QLatin1StringView(spell[expected[s]]));
++ }
++ //: <options so far>, or '<final option>'
++ exp_str = QXmlStream::tr("%1, or '%2'", "expected")
++ .arg(exp_str, QLatin1StringView(spell[expected[s]]));
++ }
++ error_message = QXmlStream::tr("Expected %1, but got '%2'.")
++ .arg(exp_str, QLatin1StringView(spell[token]));
++ } else {
++ error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1StringView(spell[token]));
++ }
++
++ raiseWellFormedError(error_message);
++ }
++
++ void QXmlStreamReaderPrivate::resume(int rule) {
++ resumeReduction = rule;
++ if (error == QXmlStreamReader::NoError)
++ raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
++ }
++
++ /*! Returns the current line number, starting with 1.
++
++ \sa columnNumber(), characterOffset()
++ */
++ qint64 QXmlStreamReader::lineNumber() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->lineNumber + 1; // in public we start with 1
++ }
++
++ /*! Returns the current column number, starting with 0.
++
++ \sa lineNumber(), characterOffset()
++ */
++ qint64 QXmlStreamReader::columnNumber() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->characterOffset - d->lastLineStart + d->readBufferPos;
++ }
++
++ /*! Returns the current character offset, starting with 0.
++
++ \sa lineNumber(), columnNumber()
++ */
++ qint64 QXmlStreamReader::characterOffset() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->characterOffset + d->readBufferPos;
++ }
++
++
++ /*! Returns the text of \l Characters, \l Comment, \l DTD, or
++ EntityReference.
++ */
++ QStringView QXmlStreamReader::text() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->text;
++ }
++
++
++ /*! If the tokenType() is \l DTD, this function returns the DTD's
++ notation declarations. Otherwise an empty vector is returned.
++
++ The QXmlStreamNotationDeclarations class is defined to be a QList
++ of QXmlStreamNotationDeclaration.
++ */
++ QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (!d->scanDtd && !d->notationDeclarations.isEmpty())
++ const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
++ return d->publicNotationDeclarations;
++ }
++
++
++ /*! If the tokenType() is \l DTD, this function returns the DTD's
++ unparsed (external) entity declarations. Otherwise an empty vector is returned.
++
++ The QXmlStreamEntityDeclarations class is defined to be a QList
++ of QXmlStreamEntityDeclaration.
++ */
++ QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (!d->scanDtd && !d->entityDeclarations.isEmpty())
++ const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
++ return d->publicEntityDeclarations;
++ }
++
++ /*!
++ \since 4.4
++
++ If the tokenType() is \l DTD, this function returns the DTD's
++ name. Otherwise an empty string is returned.
++
++ */
++ QStringView QXmlStreamReader::dtdName() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::DTD)
++ return d->dtdName;
++ return QStringView();
++ }
++
++ /*!
++ \since 4.4
++
++ If the tokenType() is \l DTD, this function returns the DTD's
++ public identifier. Otherwise an empty string is returned.
++
++ */
++ QStringView QXmlStreamReader::dtdPublicId() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::DTD)
++ return d->dtdPublicId;
++ return QStringView();
++ }
++
++ /*!
++ \since 4.4
++
++ If the tokenType() is \l DTD, this function returns the DTD's
++ system identifier. Otherwise an empty string is returned.
++
++ */
++ QStringView QXmlStreamReader::dtdSystemId() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::DTD)
++ return d->dtdSystemId;
++ return QStringView();
++ }
++
++ /*!
++ \since 5.15
++
++ Returns the maximum amount of characters a single entity is
++ allowed to expand into. If a single entity expands past the
++ given limit, the document is not considered well formed.
++
++ \sa setEntityExpansionLimit
++ */
++ int QXmlStreamReader::entityExpansionLimit() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->entityExpansionLimit;
++ }
++
++ /*!
++ \since 5.15
++
++ Sets the maximum amount of characters a single entity is
++ allowed to expand into to \a limit. If a single entity expands
++ past the given limit, the document is not considered well formed.
++
++ The limit is there to prevent DoS attacks when loading unknown
++ XML documents where recursive entity expansion could otherwise
++ exhaust all available memory.
++
++ The default value for this property is 4096 characters.
++
++ The minimum is one (1), to support the built-in XML entities (\c{<} etc).
++
++ \sa entityExpansionLimit
++ */
++ void QXmlStreamReader::setEntityExpansionLimit(int limit)
++ {
++ Q_D(QXmlStreamReader);
++ if (limit < 1) {
++ qWarning("QXmlStreamReader::setEntityExpansionLimit(): "
++ "limit %d is out of range [1:INT_MAX], clamping to 1.", limit);
++ }
++ d->entityExpansionLimit = (std::max)(limit, 1);
++ }
++
++ /*! If the tokenType() is \l StartElement, this function returns the
++ element's namespace declarations. Otherwise an empty vector is
++ returned.
++
++ The QXmlStreamNamespaceDeclarations class is defined to be a QList
++ of QXmlStreamNamespaceDeclaration.
++
++ \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations()
++ */
++ QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement)
++ const_cast<QXmlStreamReaderPrivate *>(d)->resolvePublicNamespaces();
++ return d->publicNamespaceDeclarations;
++ }
++
++
++ /*!
++ \since 4.4
++
++ Adds an \a extraNamespaceDeclaration. The declaration will be
++ valid for children of the current element, or - should the function
++ be called before any elements are read - for the entire XML
++ document.
++
++ \sa namespaceDeclarations(), addExtraNamespaceDeclarations(), setNamespaceProcessing()
++ */
++ void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration)
++ {
++ Q_D(QXmlStreamReader);
++ QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
++ namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix());
++ namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri());
++ }
++
++ /*!
++ \since 4.4
++
++ Adds a vector of declarations specified by \a extraNamespaceDeclarations.
++
++ \sa namespaceDeclarations(), addExtraNamespaceDeclaration()
++ */
++ void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations)
++ {
++ for (const auto &extraNamespaceDeclaration : extraNamespaceDeclarations)
++ addExtraNamespaceDeclaration(extraNamespaceDeclaration);
++ }
++
++
++ /*! Convenience function to be called in case a StartElement was
++ read. Reads until the corresponding EndElement and returns all text
++ in-between. In case of no error, the current token (see tokenType())
++ after having called this function is EndElement.
++
++ The function concatenates text() when it reads either \l Characters
++ or EntityReference tokens, but skips ProcessingInstruction and \l
++ Comment. If the current token is not StartElement, an empty string is
++ returned.
++
++ The \a behaviour defines what happens in case anything else is
++ read before reaching EndElement. The function can include the text from
++ child elements (useful for example for HTML), ignore child elements, or
++ raise an UnexpectedElementError and return what was read so far (default).
++
++ \since 4.6
++ */
++ QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour)
++ {
++ Q_D(QXmlStreamReader);
++ if (isStartElement()) {
++ QString result;
++ qsizetype nestingLevel = 1;
++ do {
++ switch (readNext()) {
++ case Characters:
++ case EntityReference:
++ result.insert(result.size(), d->text);
++ break;
++ case EndElement:
++ --nestingLevel;
++ break;
++ case ProcessingInstruction:
++ case Comment:
++ break;
++ case StartElement:
++ if (behaviour == SkipChildElements) {
++ skipCurrentElement();
++ break;
++ } else if (behaviour == IncludeChildElements) {
++ ++nestingLevel;
++ break;
++ }
++ Q_FALLTHROUGH();
++ default:
++ if (d->error || behaviour == ErrorOnUnexpectedElement) {
++ if (!d->error)
++ d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data."));
++ return result;
++ }
++ }
++ } while (nestingLevel);
++ return result;
++ }
++ return QString();
++ }
++
++ /*! Raises a custom error with an optional error \a message.
++
++ \sa error(), errorString()
++ */
++ void QXmlStreamReader::raiseError(const QString& message)
++ {
++ Q_D(QXmlStreamReader);
++ d->raiseError(CustomError, message);
++ }
++
++ /*!
++ Returns the error message that was set with raiseError().
++
++ \sa error(), lineNumber(), columnNumber(), characterOffset()
++ */
++ QString QXmlStreamReader::errorString() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::Invalid)
++ return d->errorString;
++ return QString();
++ }
++
++ /*! Returns the type of the current error, or NoError if no error occurred.
++
++ \sa errorString(), raiseError()
++ */
++ QXmlStreamReader::Error QXmlStreamReader::error() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::Invalid)
++ return d->error;
++ return NoError;
++ }
++
++ /*!
++ Returns the target of a ProcessingInstruction.
++ */
++ QStringView QXmlStreamReader::processingInstructionTarget() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->processingInstructionTarget;
++ }
++
++ /*!
++ Returns the data of a ProcessingInstruction.
++ */
++ QStringView QXmlStreamReader::processingInstructionData() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->processingInstructionData;
++ }
++
++
++
++ /*!
++ Returns the local name of a StartElement, EndElement, or an EntityReference.
++
++ \sa namespaceUri(), qualifiedName()
++ */
++ QStringView QXmlStreamReader::name() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->name;
++ }
++
++ /*!
++ Returns the namespaceUri of a StartElement or EndElement.
++
++ \sa name(), qualifiedName()
++ */
++ QStringView QXmlStreamReader::namespaceUri() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->namespaceUri;
++ }
++
++ /*!
++ Returns the qualified name of a StartElement or EndElement;
++
++ A qualified name is the raw name of an element in the XML data. It
++ consists of the namespace prefix, followed by colon, followed by the
++ element's local name. Since the namespace prefix is not unique (the
++ same prefix can point to different namespaces and different prefixes
++ can point to the same namespace), you shouldn't use qualifiedName(),
++ but the resolved namespaceUri() and the attribute's local name().
++
++ \sa name(), prefix(), namespaceUri()
++ */
++ QStringView QXmlStreamReader::qualifiedName() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->qualifiedName;
++ }
++
++
++
++ /*!
++ \since 4.4
++
++ Returns the prefix of a StartElement or EndElement.
++
++ \sa name(), qualifiedName()
++ */
++ QStringView QXmlStreamReader::prefix() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->prefix;
++ }
++
++ /*!
++ Returns the attributes of a StartElement.
++ */
++ QXmlStreamAttributes QXmlStreamReader::attributes() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->attributes;
++ }
++
++ #endif // feature xmlstreamreader
++
++ /*!
++ \class QXmlStreamAttribute
++ \inmodule QtCore
++ \since 4.3
++ \reentrant
++ \brief The QXmlStreamAttribute class represents a single XML attribute.
++
++ \ingroup xml-tools
++
++ \compares equality
++
++ An attribute consists of an optionally empty namespaceUri(), a
++ name(), a value(), and an isDefault() attribute.
++
++ The raw XML attribute name is returned as qualifiedName().
++ */
++
++ /*!
++ Creates an empty attribute.
++ */
++ QXmlStreamAttribute::QXmlStreamAttribute()
++ {
++ m_isDefault = false;
++ }
++
++ /*! Constructs an attribute in the namespace described with \a
++ namespaceUri with \a name and value \a value.
++
++ The attribute will have isDefault() == \c{false}.
++ */
++ QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)
++ : m_isDefault(false)
++ {
++ m_namespaceUri = namespaceUri;
++ m_name = m_qualifiedName = name;
++ m_value = value;
++ }
++
++ /*!
++ Constructs an attribute with qualified name \a qualifiedName and value \a value.
++
++ The attribute will have isDefault() == \c{false}.
++ */
++ QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value)
++ : m_isDefault(false)
++ {
++ qsizetype colon = qualifiedName.indexOf(u':');
++ m_name = qualifiedName.mid(colon + 1);
++ m_qualifiedName = qualifiedName;
++ m_value = value;
++ }
++
++ /*! \fn QStringView QXmlStreamAttribute::namespaceUri() const
++
++ Returns the attribute's resolved namespaceUri, or an empty string
++ reference if the attribute does not have a defined namespace.
++ */
++ /*! \fn QStringView QXmlStreamAttribute::name() const
++ Returns the attribute's local name.
++ */
++ /*! \fn QStringView QXmlStreamAttribute::qualifiedName() const
++ Returns the attribute's qualified name.
++
++ A qualified name is the raw name of an attribute in the XML
++ data. It consists of the namespace prefix(), followed by colon,
++ followed by the attribute's local name(). Since the namespace prefix
++ is not unique (the same prefix can point to different namespaces
++ and different prefixes can point to the same namespace), you
++ shouldn't use qualifiedName(), but the resolved namespaceUri() and
++ the attribute's local name().
++ */
++ /*!
++ \fn QStringView QXmlStreamAttribute::prefix() const
++ \since 4.4
++ Returns the attribute's namespace prefix.
++
++ \sa name(), qualifiedName()
++
++ */
++
++ /*! \fn QStringView QXmlStreamAttribute::value() const
++ Returns the attribute's value.
++ */
++
++ /*! \fn bool QXmlStreamAttribute::isDefault() const
++
++ Returns \c true if the parser added this attribute with a default
++ value following an ATTLIST declaration in the DTD; otherwise
++ returns \c false.
++ */
++ /*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &lhs, const QXmlStreamAttribute &rhs)
++
++ Compares \a lhs attribute with \a rhs and returns \c true if they are
++ equal; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &lhs, const QXmlStreamAttribute &rhs)
++
++ Compares \a lhs attribute with \a rhs and returns \c true if they are
++ not equal; otherwise returns \c false.
++ */
++
++ /*!
++ \class QXmlStreamAttributes
++ \inmodule QtCore
++ \since 4.3
++ \reentrant
++ \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute.
++
++ Attributes are returned by a QXmlStreamReader in
++ \l{QXmlStreamReader::attributes()} {attributes()} when the reader
++ reports a \l {QXmlStreamReader::StartElement}{start element}. The
++ class can also be used with a QXmlStreamWriter as an argument to
++ \l {QXmlStreamWriter::writeAttributes()}{writeAttributes()}.
++
++ The convenience function value() loops over the vector and returns
++ an attribute value for a given namespaceUri and an attribute's
++ name.
++
++ New attributes can be added with append().
++
++ \ingroup xml-tools
++ */
++
++ /*!
++ \fn QXmlStreamAttributes::QXmlStreamAttributes()
++
++ A constructor for QXmlStreamAttributes.
++ */
++
++ /*!
++ \typedef QXmlStreamNotationDeclarations
++ \relates QXmlStreamNotationDeclaration
++
++ Synonym for QList<QXmlStreamNotationDeclaration>.
++ */
++
++
++ /*!
++ \class QXmlStreamNotationDeclaration
++ \inmodule QtCore
++ \since 4.3
++ \reentrant
++ \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration.
++
++ \ingroup xml-tools
++
++ \compares equality
++
++ An notation declaration consists of a name(), a systemId(), and a publicId().
++ */
++
++ /*!
++ Creates an empty notation declaration.
++ */
++ QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration()
++ {
++ }
++
++ /*! \fn QStringView QXmlStreamNotationDeclaration::name() const
++
++ Returns the notation name.
++ */
++ /*! \fn QStringView QXmlStreamNotationDeclaration::systemId() const
++
++ Returns the system identifier.
++ */
++ /*! \fn QStringView QXmlStreamNotationDeclaration::publicId() const
++
++ Returns the public identifier.
++ */
++
++ /*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &lhs, const QXmlStreamNotationDeclaration &rhs)
++
++ Compares \a lhs notation declaration with \a rhs and returns \c true
++ if they are equal; otherwise returns \c false.
++ */
++ /*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &lhs, const QXmlStreamNotationDeclaration &rhs)
++
++ Compares \a lhs notation declaration with \a rhs and returns \c true
++ if they are not equal; otherwise returns \c false.
++ */
++
++ /*!
++ \typedef QXmlStreamNamespaceDeclarations
++ \relates QXmlStreamNamespaceDeclaration
++
++ Synonym for QList<QXmlStreamNamespaceDeclaration>.
++ */
++
++ /*!
++ \class QXmlStreamNamespaceDeclaration
++ \inmodule QtCore
++ \since 4.3
++ \reentrant
++ \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration.
++
++ \ingroup xml-tools
++
++ \compares equality
++
++ An namespace declaration consists of a prefix() and a namespaceUri().
++ */
++ /*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &lhs, const QXmlStreamNamespaceDeclaration &rhs)
++
++ Compares \a lhs namespace declaration with \a rhs and returns \c true
++ if they are equal; otherwise returns \c false.
++ */
++ /*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &lhs, const QXmlStreamNamespaceDeclaration &rhs)
++
++ Compares \a lhs namespace declaration with \a rhs and returns \c true
++ if they are not equal; otherwise returns \c false.
++ */
++
++ /*!
++ Creates an empty namespace declaration.
++ */
++ QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration()
++ {
++ }
++
++ /*!
++ \since 4.4
++
++ Creates a namespace declaration with \a prefix and \a namespaceUri.
++ */
++ QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri)
++ {
++ m_prefix = prefix;
++ m_namespaceUri = namespaceUri;
++ }
++
++ /*! \fn QStringView QXmlStreamNamespaceDeclaration::prefix() const
++
++ Returns the prefix.
++ */
++ /*! \fn QStringView QXmlStreamNamespaceDeclaration::namespaceUri() const
++
++ Returns the namespaceUri.
++ */
++
++
++
++
++ /*!
++ \typedef QXmlStreamEntityDeclarations
++ \relates QXmlStreamEntityDeclaration
++
++ Synonym for QList<QXmlStreamEntityDeclaration>.
++ */
++
++ /*!
++ \class QXmlString
++ \inmodule QtCore
++ \since 6.0
++ \internal
++ */
++
++ /*!
++ \class QXmlStreamEntityDeclaration
++ \inmodule QtCore
++ \since 4.3
++ \reentrant
++ \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration.
++
++ \ingroup xml-tools
++
++ \compares equality
++ An entity declaration consists of a name(), a notationName(), a
++ systemId(), a publicId(), and a value().
++ */
++
++ /*!
++ Creates an empty entity declaration.
++ */
++ QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration()
++ {
++ }
++
++ /*! \fn QStringView QXmlStreamEntityDeclaration::name() const
++
++ Returns the entity name.
++ */
++ /*! \fn QStringView QXmlStreamEntityDeclaration::notationName() const
++
++ Returns the notation name.
++ */
++ /*! \fn QStringView QXmlStreamEntityDeclaration::systemId() const
++
++ Returns the system identifier.
++ */
++ /*! \fn QStringView QXmlStreamEntityDeclaration::publicId() const
++
++ Returns the public identifier.
++ */
++ /*! \fn QStringView QXmlStreamEntityDeclaration::value() const
++
++ Returns the entity's value.
++ */
++
++ /*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &lhs, const QXmlStreamEntityDeclaration &rhs)
++
++ Compares \a lhs entity declaration with \a rhs and returns \c true if
++ they are equal; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &lhs, const QXmlStreamEntityDeclaration &rhs)
++
++ Compares \a lhs entity declaration with \a rhs and returns \c true if
++ they are not equal; otherwise returns \c false.
++ */
++
++ /*! Returns the value of the attribute \a name in the namespace
++ described with \a namespaceUri, or an empty string reference if the
++ attribute is not defined. The \a namespaceUri can be empty.
++
++ \note In Qt versions prior to 6.6, this function was implemented as an
++ overload set accepting combinations of QString and QLatin1StringView only.
++ */
++ QStringView QXmlStreamAttributes::value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
++ {
++ for (const QXmlStreamAttribute &attribute : *this) {
++ if (attribute.name() == name && attribute.namespaceUri() == namespaceUri)
++ return attribute.value();
++ }
++ return QStringView();
++ }
++
++ /*!\overload
++
++ Returns the value of the attribute with qualified name \a
++ qualifiedName , or an empty string reference if the attribute is not
++ defined. A qualified name is the raw name of an attribute in the XML
++ data. It consists of the namespace prefix, followed by colon,
++ followed by the attribute's local name. Since the namespace prefix
++ is not unique (the same prefix can point to different namespaces and
++ different prefixes can point to the same namespace), you shouldn't
++ use qualified names, but a resolved namespaceUri and the attribute's
++ local name.
++
++ \note In Qt versions prior to 6.6, this function was implemented as an
++ overload set accepting QString and QLatin1StringView only.
++
++ */
++ QStringView QXmlStreamAttributes::value(QAnyStringView qualifiedName) const noexcept
++ {
++ for (const QXmlStreamAttribute &attribute : *this) {
++ if (attribute.qualifiedName() == qualifiedName)
++ return attribute.value();
++ }
++ return QStringView();
++ }
++
++ /*!Appends a new attribute with \a name in the namespace
++ described with \a namespaceUri, and value \a value. The \a
++ namespaceUri can be empty.
++ */
++ void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value)
++ {
++ append(QXmlStreamAttribute(namespaceUri, name, value));
++ }
++
++ /*!\overload
++ Appends a new attribute with qualified name \a qualifiedName and
++ value \a value.
++ */
++ void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value)
++ {
++ append(QXmlStreamAttribute(qualifiedName, value));
++ }
++
++ #if QT_CONFIG(xmlstreamreader)
++
++ /*! \fn bool QXmlStreamReader::isStartDocument() const
++ Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isEndDocument() const
++ Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isStartElement() const
++ Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isEndElement() const
++ Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isCharacters() const
++ Returns \c true if tokenType() equals \l Characters; otherwise returns \c false.
++
++ \sa isWhitespace(), isCDATA()
++ */
++ /*! \fn bool QXmlStreamReader::isComment() const
++ Returns \c true if tokenType() equals \l Comment; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isDTD() const
++ Returns \c true if tokenType() equals \l DTD; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isEntityReference() const
++ Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false.
++ */
++ /*! \fn bool QXmlStreamReader::isProcessingInstruction() const
++ Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false.
++ */
++
++ /*! Returns \c true if the reader reports characters that only consist
++ of white-space; otherwise returns \c false.
++
++ \sa isCharacters(), text()
++ */
++ bool QXmlStreamReader::isWhitespace() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->type == QXmlStreamReader::Characters && d->isWhitespace;
++ }
++
++ /*! Returns \c true if the reader reports characters that stem from a
++ CDATA section; otherwise returns \c false.
++
++ \sa isCharacters(), text()
++ */
++ bool QXmlStreamReader::isCDATA() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->type == QXmlStreamReader::Characters && d->isCDATA;
++ }
++
++
++
++ /*!
++ Returns \c true if this document has been declared standalone in the
++ XML declaration; otherwise returns \c false.
++
++ If no XML declaration has been parsed, this function returns \c false.
++
++ \sa hasStandaloneDeclaration()
++ */
++ bool QXmlStreamReader::isStandaloneDocument() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->standalone;
++ }
++
++ /*!
++ \since 6.6
++
++ Returns \c true if this document has an explicit standalone
++ declaration (can be 'yes' or 'no'); otherwise returns \c false;
++
++ If no XML declaration has been parsed, this function returns \c false.
++
++ \sa isStandaloneDocument()
++ */
++ bool QXmlStreamReader::hasStandaloneDeclaration() const
++ {
++ Q_D(const QXmlStreamReader);
++ return d->hasStandalone;
++ }
++
++ /*!
++ \since 4.4
++
++ If the tokenType() is \l StartDocument, this function returns the
++ version string as specified in the XML declaration.
++ Otherwise an empty string is returned.
++ */
++ QStringView QXmlStreamReader::documentVersion() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::StartDocument)
++ return d->documentVersion;
++ return QStringView();
++ }
++
++ /*!
++ \since 4.4
++
++ If the tokenType() is \l StartDocument, this function returns the
++ encoding string as specified in the XML declaration.
++ Otherwise an empty string is returned.
++ */
++ QStringView QXmlStreamReader::documentEncoding() const
++ {
++ Q_D(const QXmlStreamReader);
++ if (d->type == QXmlStreamReader::StartDocument)
++ return d->documentEncoding;
++ return QStringView();
++ }
++
++ #endif // feature xmlstreamreader
++
++ /*!
++ \class QXmlStreamWriter
++ \inmodule QtCore
++ \since 4.3
++ \reentrant
++
++ \brief The QXmlStreamWriter class provides an XML 1.0 writer with a
++ simple streaming API.
++
++ \ingroup xml-tools
++ \ingroup qtserialization
++
++ QXmlStreamWriter is the counterpart to QXmlStreamReader for writing
++ XML.
++ It is compliant with the XML 1.0 specification and writes documents
++ using XML 1.0 syntax, escaping rules, and character validity
++ constraints.
++ \note XML 1.1 is not supported. While version strings may be set
++ manually in the output, documents requiring features specific to
++ XML 1.1, such as additional control characters cannot be produced
++ using this class.
++
++ Like its related class, it operates on a QIODevice specified
++ with setDevice(). The API is simple and straightforward: for every
++ XML token or event you want to write, the writer provides a
++ specialized function.
++
++ You start a document with writeStartDocument() and end it with
++ writeEndDocument(). This will implicitly close all remaining open
++ tags.
++
++ Element tags are opened with writeStartElement() followed by
++ writeAttribute() or writeAttributes(), element content, and then
++ writeEndElement(). A shorter form writeEmptyElement() can be used
++ to write empty elements, followed by writeAttributes().
++
++ Element content consists of either characters, entity references or
++ nested elements. It is written with writeCharacters(), which also
++ takes care of escaping all forbidden characters and character
++ sequences, writeEntityReference(), or subsequent calls to
++ writeStartElement(). A convenience method writeTextElement() can be
++ used for writing terminal elements that contain nothing but text.
++
++ The following abridged code snippet shows the basic use of the class
++ to write formatted XML with indentation:
++
++ \snippet qxmlstreamwriter/main.cpp start stream
++ \dots
++ \snippet qxmlstreamwriter/main.cpp write element
++ \dots
++ \snippet qxmlstreamwriter/main.cpp finish stream
++
++ QXmlStreamWriter takes care of prefixing namespaces, all you have to
++ do is specify the \c namespaceUri when writing elements or
++ attributes. If you must conform to certain prefixes, you can force
++ the writer to use them by declaring the namespaces manually with
++ either writeNamespace() or writeDefaultNamespace(). Alternatively,
++ you can bypass the stream writer's namespace support and use
++ overloaded methods that take a qualified name instead. The namespace
++ \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the
++ prefix \e xml.
++
++ The stream writer can automatically format the generated XML data by
++ adding line-breaks and indentation to empty sections between
++ elements, making the XML data more readable for humans and easier to
++ work with for most source code management systems. The feature can
++ be turned on with the \l autoFormatting property, and customized
++ with the \l autoFormattingIndent property.
++
++ Other functions are writeCDATA(), writeComment(),
++ writeProcessingInstruction(), and writeDTD(). Chaining of XML
++ streams is supported with writeCurrentToken().
++
++ QXmlStreamWriter always encodes XML in UTF-8.
++
++ If an error occurs while writing, \l hasError() will return true.
++ However, by default, data that was already buffered at the time the error
++ occurred, or data written from within the same operation, may still be
++ written to the underlying device. This applies to \l Error::Encoding,
++ \l Error::InvalidCharacter, and user-raised \l Error::Custom.
++ To avoid this and ensure no data is written after an error, use the
++ \l stopWritingOnError property. When this property is enabled,
++ the first error stops output immediately and the writer ignores all
++ subsequent write operations.
++ Applications should treat the error state as terminal and avoid further
++ use of the writer after an error.
++
++ The \l{QXmlStream Bookmarks Example} illustrates how to use a
++ stream writer to write an XML bookmark file (XBEL) that
++ was previously read in by a QXmlStreamReader.
++
++ */
++
++ /*!
++ \enum QXmlStreamWriter::Error
++
++ This enum specifies the different error cases that can occur
++ when writing XML with QXmlStreamWriter.
++
++ \value None No error has occurred.
++
++ \value IO An I/O error occurred while writing to the
++ device.
++
++ \value Encoding An encoding error occurred while converting
++ characters to the output format.
++
++ \value InvalidCharacter A character not permitted in XML 1.0
++ was encountered while writing.
++
++ \value Custom A custom error has been raised with
++ \l raiseError().
++
++ \since 6.10
++ */
++
++ #if QT_CONFIG(xmlstreamwriter)
++
++ class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
++ {
++ QXmlStreamWriter *q_ptr;
++ Q_DECLARE_PUBLIC(QXmlStreamWriter)
++ public:
++ enum class StartElementOption {
++ KeepEverything = 0, // write out every attribute, namespace, &c.
++ OmitNamespaceDeclarations = 1,
++ };
++
++ QXmlStreamWriterPrivate(QXmlStreamWriter *q);
++ ~QXmlStreamWriterPrivate() {
++ if (deleteDevice)
++ delete device;
++ }
++
++ void raiseError(QXmlStreamWriter::Error error);
++ void raiseError(QXmlStreamWriter::Error error, QAnyStringView message);
++ void write(QAnyStringView s);
++ void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
++ bool finishStartElement(bool contents = true);
++ void writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
++ StartElementOption option = StartElementOption::KeepEverything);
++ QIODevice *device = nullptr;
++ QString *stringDevice = nullptr;
++ uint deleteDevice :1;
++ uint inStartElement :1;
++ uint inEmptyElement :1;
++ uint lastWasStartElement :1;
++ uint wroteSomething :1;
++ uint autoFormatting :1;
++ uint didWriteStartDocument :1;
++ uint didWriteAnyToken :1;
++ uint stopWritingOnError :1;
++ std::string autoFormattingIndent = std::string(4, ' ');
++ NamespaceDeclaration emptyNamespace;
++ qsizetype lastNamespaceDeclaration = 1;
++ QXmlStreamWriter::Error error = QXmlStreamWriter::Error::None;
++ QString errorString;
++
++ NamespaceDeclaration &addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix);
++ NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
++ void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration);
++
++ int namespacePrefixCount = 0;
++
++ void indent(int level);
++ private:
++ void doWriteToDevice(QStringView s);
++ void doWriteToDevice(QUtf8StringView s);
++ void doWriteToDevice(QLatin1StringView s);
++ };
++
++
++ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
++ : q_ptr(q), deleteDevice(false), inStartElement(false),
++ inEmptyElement(false), lastWasStartElement(false),
++ wroteSomething(false), autoFormatting(false),
++ didWriteStartDocument(false), didWriteAnyToken(false),
++ stopWritingOnError(false)
++ {
++ }
++
++ void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode)
++ {
++ error = errorCode;
++ switch (error) {
++ case QXmlStreamWriter::Error::IO:
++ errorString = QXmlStream::tr("An I/O error occurred while writing");
++ break;
++ case QXmlStreamWriter::Error::Encoding:
++ errorString = QXmlStream::tr("An encoding error occurred while writing");
++ break;
++ case QXmlStreamWriter::Error::InvalidCharacter:
++ errorString = QXmlStream::tr("Encountered an invalid XML 1.0 character while writing");
++ break;
++ case QXmlStreamWriter::Error::Custom:
++ errorString = QXmlStream::tr("An error occurred while writing");
++ break;
++ case QXmlStreamWriter::Error::None:
++ errorString.clear();
++ break;
++ }
++ }
++
++ void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode, QAnyStringView message)
++ {
++ error = errorCode;
++ errorString = message.toString();
++ }
++
++ void QXmlStreamWriterPrivate::write(QAnyStringView s)
++ {
++ if (stopWritingOnError && (error != QXmlStreamWriter::Error::None))
++ return;
++ if (device) {
++ if (error == QXmlStreamWriter::Error::IO)
++ return;
++
++ s.visit([&] (auto s) { doWriteToDevice(s); });
++ } else if (stringDevice) {
++ s.visit([&] (auto s) { stringDevice->append(s); });
++ } else {
++ qWarning("QXmlStreamWriter: No device");
++ }
++ }
++
++ void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
++ {
++ struct NextResult {
++ char32_t value;
++ bool encodingError;
++ };
++ struct NextLatin1 {
++ NextResult operator()(const char *&it, const char *) const
++ { return {uchar(*it++), false}; }
++ };
++ struct NextUtf8 {
++ NextResult operator()(const char *&it, const char *end) const
++ {
++ // We can have '\0' in the text, and it should be reported as
++ // Error::InvalidCharacter, not as Error::Encoding
++ constexpr char32_t invalidValue = 0xFFFFFFFF;
++ static_assert(invalidValue > QChar::LastValidCodePoint);
++ auto i = reinterpret_cast<const qchar8_t *>(it);
++ const auto old_i = i;
++ const auto e = reinterpret_cast<const qchar8_t *>(end);
++ const char32_t result = QUtf8Functions::nextUcs4FromUtf8(i, e, invalidValue);
++ it += i - old_i;
++ return result == invalidValue ? NextResult{U'\0', true}
++ : NextResult{result, false};
++ }
++ };
++ struct NextUtf16 {
++ NextResult operator()(const QChar *&it, const QChar *end) const
++ {
++ QStringIterator decoder(it, end);
++ // We can have '\0' in the text, and it should be reported as
++ // Error::InvalidCharacter, not as Error::Encoding
++ constexpr char32_t invalidValue = 0xFFFFFFFF;
++ static_assert(invalidValue > QChar::LastValidCodePoint);
++ char32_t result = decoder.next(invalidValue);
++ it = decoder.position();
++ return result == invalidValue ? NextResult{U'\0', true}
++ : NextResult{result, false};
++ }
++ };
++
++ QString escaped;
++ escaped.reserve(s.size());
++ s.visit([&] (auto s) {
++ using View = decltype(s);
++ using Decoder = std::conditional_t<std::is_same_v<View, QLatin1StringView>, NextLatin1,
++ std::conditional_t<std::is_same_v<View, QUtf8StringView>, NextUtf8, NextUtf16>>;
++
++ auto it = s.begin();
++ const auto end = s.end();
++ Decoder decoder;
++
++ while (it != end) {
++ QLatin1StringView replacement;
++ auto mark = it;
++
++ while (it != end) {
++ auto next_it = it;
++ const auto decoded = decoder(next_it, end);
++ switch (decoded.value) {
++ case u'<':
++ replacement = "<"_L1;
++ break;
++ case u'>':
++ replacement = ">"_L1;
++ break;
++ case u'&':
++ replacement = "&"_L1;
++ break;
++ case u'\"':
++ replacement = """_L1;
++ break;
++ case u'\t':
++ if (escapeWhitespace)
++ replacement = "	"_L1;
++ break;
++ case u'\n':
++ if (escapeWhitespace)
++ replacement = " "_L1;
++ break;
++ case u'\r':
++ if (escapeWhitespace)
++ replacement = " "_L1;
++ break;
++ case u'\v':
++ case u'\f':
++ raiseError(QXmlStreamWriter::Error::InvalidCharacter);
++ if (stopWritingOnError)
++ return;
++ replacement = ""_L1;
++ Q_ASSERT(!replacement.isNull());
++ break;
++ default:
++ if (decoded.value > 0x1F)
++ break;
++ // ASCII control characters
++ Q_FALLTHROUGH();
++ case 0xFFFE:
++ case 0xFFFF:
++ raiseError(decoded.encodingError
++ ? QXmlStreamWriter::Error::Encoding
++ : QXmlStreamWriter::Error::InvalidCharacter);
++ if (stopWritingOnError)
++ return;
++ replacement = ""_L1;
++ Q_ASSERT(!replacement.isNull());
++ break;
++ }
++ if (!replacement.isNull())
++ break;
++ it = next_it;
++ }
++
++ escaped.append(View{mark, it});
++ escaped.append(replacement);
++ if (it != end)
++ ++it;
++ }
++ } );
++
++ write(escaped);
++ }
++
++ void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) {
++ if (namespaceDeclaration.prefix.isEmpty()) {
++ write(" xmlns=\"");
++ write(namespaceDeclaration.namespaceUri);
++ write("\"");
++ } else {
++ write(" xmlns:");
++ write(namespaceDeclaration.prefix);
++ write("=\"");
++ write(namespaceDeclaration.namespaceUri);
++ write("\"");
++ }
++ didWriteAnyToken = true;
++ }
++
++ bool QXmlStreamWriterPrivate::finishStartElement(bool contents)
++ {
++ bool hadSomethingWritten = wroteSomething;
++ wroteSomething = contents;
++ if (!inStartElement)
++ return hadSomethingWritten;
++
++ if (inEmptyElement) {
++ write("/>");
++ QXmlStreamWriterPrivate::Tag tag = tagStack_pop();
++ lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
++ lastWasStartElement = false;
++ } else {
++ write(">");
++ }
++ inStartElement = inEmptyElement = false;
++ lastNamespaceDeclaration = namespaceDeclarations.size();
++ didWriteAnyToken = true;
++ return hadSomethingWritten;
++ }
++
++ QXmlStreamPrivateTagStack::NamespaceDeclaration &
++ QXmlStreamWriterPrivate::addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
++ {
++ const bool prefixIsXml = prefix == "xml"_L1;
++ const bool namespaceUriIsXml = namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1;
++ if (prefixIsXml && !namespaceUriIsXml) {
++ qWarning("Reserved prefix 'xml' must not be bound to a different namespace name "
++ "than 'http://www.w3.org/XML/1998/namespace'");
++ } else if (!prefixIsXml && namespaceUriIsXml) {
++ const QString prefixString = prefix.toString();
++ qWarning("The prefix '%ls' must not be bound to namespace name "
++ "'http://www.w3.org/XML/1998/namespace' which 'xml' is already bound to",
++ qUtf16Printable(prefixString));
++ }
++ if (namespaceUri == "http://www.w3.org/2000/xmlns/"_L1) {
++ const QString prefixString = prefix.toString();
++ qWarning("The prefix '%ls' must not be bound to namespace name "
++ "'http://www.w3.org/2000/xmlns/'",
++ qUtf16Printable(prefixString));
++ }
++ auto &namespaceDeclaration = namespaceDeclarations.push();
++ namespaceDeclaration.prefix = addToStringStorage(prefix);
++ namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
++ return namespaceDeclaration;
++ }
++
++ QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(QAnyStringView namespaceUri, bool writeDeclaration, bool noDefault)
++ {
++ for (NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
++ if (namespaceDeclaration.namespaceUri == namespaceUri) {
++ if (!noDefault || !namespaceDeclaration.prefix.isEmpty())
++ return namespaceDeclaration;
++ }
++ }
++ if (namespaceUri.isEmpty())
++ return emptyNamespace;
++ NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
++ if (namespaceUri.isEmpty()) {
++ namespaceDeclaration.prefix.clear();
++ } else {
++ QString s;
++ int n = ++namespacePrefixCount;
++ forever {
++ s = u'n' + QString::number(n++);
++ qsizetype j = namespaceDeclarations.size() - 2;
++ while (j >= 0 && namespaceDeclarations.at(j).prefix != s)
++ --j;
++ if (j < 0)
++ break;
++ }
++ namespaceDeclaration.prefix = addToStringStorage(s);
++ }
++ namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
++ if (writeDeclaration)
++ writeNamespaceDeclaration(namespaceDeclaration);
++ return namespaceDeclaration;
++ }
++
++
++
++ void QXmlStreamWriterPrivate::indent(int level)
++ {
++ if (didWriteStartDocument || didWriteAnyToken)
++ write("\n");
++ for (int i = 0; i < level; ++i)
++ write(autoFormattingIndent);
++ }
++
++ void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
++ {
++ constexpr qsizetype MaxChunkSize = 512;
++ char buffer [3 * MaxChunkSize];
++ QStringEncoder::State state;
++ while (!s.isEmpty()) {
++ const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
++ char *end = QUtf8::convertFromUnicode(buffer, s.first(chunkSize), &state);
++ doWriteToDevice(QUtf8StringView{buffer, end});
++ s = s.sliced(chunkSize);
++ }
++ if (state.remainingChars > 0)
++ raiseError(QXmlStreamWriter::Error::Encoding);
++ }
++
++ void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
++ {
++ QByteArrayView bytes = s;
++ if (device->write(bytes.data(), bytes.size()) != bytes.size())
++ raiseError(QXmlStreamWriter::Error::IO);
++ }
++
++ void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
++ {
++ constexpr qsizetype MaxChunkSize = 512;
++ char buffer [2 * MaxChunkSize];
++ while (!s.isEmpty()) {
++ const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
++ char *end = QUtf8::convertFromLatin1(buffer, s.first(chunkSize));
++ doWriteToDevice(QUtf8StringView{buffer, end});
++ s = s.sliced(chunkSize);
++ }
++ }
++
++ /*!
++ Constructs a stream writer.
++
++ \sa setDevice()
++ */
++ QXmlStreamWriter::QXmlStreamWriter()
++ : d_ptr(new QXmlStreamWriterPrivate(this))
++ {
++ }
++
++ /*!
++ Constructs a stream writer that writes into \a device;
++ */
++ QXmlStreamWriter::QXmlStreamWriter(QIODevice *device)
++ : d_ptr(new QXmlStreamWriterPrivate(this))
++ {
++ Q_D(QXmlStreamWriter);
++ d->device = device;
++ }
++
++ /*! Constructs a stream writer that writes into \a array. This is the
++ same as creating an xml writer that operates on a QBuffer device
++ which in turn operates on \a array.
++ */
++ QXmlStreamWriter::QXmlStreamWriter(QByteArray *array)
++ : d_ptr(new QXmlStreamWriterPrivate(this))
++ {
++ Q_D(QXmlStreamWriter);
++ d->device = new QBuffer(array);
++ d->device->open(QIODevice::WriteOnly);
++ d->deleteDevice = true;
++ }
++
++
++ /*! Constructs a stream writer that writes into \a string.
++ */
++ QXmlStreamWriter::QXmlStreamWriter(QString *string)
++ : d_ptr(new QXmlStreamWriterPrivate(this))
++ {
++ Q_D(QXmlStreamWriter);
++ d->stringDevice = string;
++ }
++
++ /*!
++ Destructor.
++ */
++ QXmlStreamWriter::~QXmlStreamWriter()
++ {
++ }
++
++
++ /*!
++ Sets the current device to \a device. If you want the stream to
++ write into a QByteArray, you can create a QBuffer device.
++
++ \sa device()
++ */
++ void QXmlStreamWriter::setDevice(QIODevice *device)
++ {
++ Q_D(QXmlStreamWriter);
++ if (device == d->device)
++ return;
++ d->stringDevice = nullptr;
++ if (d->deleteDevice) {
++ delete d->device;
++ d->deleteDevice = false;
++ }
++ d->device = device;
++ }
++
++ /*!
++ Returns the current device associated with the QXmlStreamWriter,
++ or \nullptr if no device has been assigned.
++
++ \sa setDevice()
++ */
++ QIODevice *QXmlStreamWriter::device() const
++ {
++ Q_D(const QXmlStreamWriter);
++ return d->device;
++ }
++
++ /*!
++ \property QXmlStreamWriter::autoFormatting
++ \since 4.4
++ \brief the auto-formatting flag of the stream writer.
++
++ This property controls whether or not the stream writer
++ automatically formats the generated XML data. If enabled, the
++ writer automatically adds line-breaks and indentation to empty
++ sections between elements (ignorable whitespace). The main purpose
++ of auto-formatting is to split the data into several lines, and to
++ increase readability for a human reader. The indentation depth can
++ be controlled through the \l autoFormattingIndent property.
++
++ By default, auto-formatting is disabled.
++ */
++
++ /*!
++ \since 4.4
++
++ Enables auto formatting if \a enable is \c true, otherwise
++ disables it.
++
++ The default value is \c false.
++ */
++ void QXmlStreamWriter::setAutoFormatting(bool enable)
++ {
++ Q_D(QXmlStreamWriter);
++ d->autoFormatting = enable;
++ }
++
++ /*!
++ \since 4.4
++
++ Returns \c true if auto formatting is enabled, otherwise \c false.
++ */
++ bool QXmlStreamWriter::autoFormatting() const
++ {
++ Q_D(const QXmlStreamWriter);
++ return d->autoFormatting;
++ }
++
++ /*!
++ \property QXmlStreamWriter::autoFormattingIndent
++ \since 4.4
++
++ \brief the number of spaces or tabs used for indentation when
++ auto-formatting is enabled. Positive numbers indicate spaces,
++ negative numbers tabs.
++
++ The default indentation is 4.
++
++ \sa autoFormatting
++ */
++
++
++ void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs)
++ {
++ Q_D(QXmlStreamWriter);
++ d->autoFormattingIndent.assign(size_t(qAbs(spacesOrTabs)), spacesOrTabs >= 0 ? ' ' : '\t');
++ }
++
++ int QXmlStreamWriter::autoFormattingIndent() const
++ {
++ Q_D(const QXmlStreamWriter);
++ const QLatin1StringView indent(d->autoFormattingIndent);
++ return indent.count(u' ') - indent.count(u'\t');
++ }
++
++ /*!
++ \property QXmlStreamWriter::stopWritingOnError
++ \since 6.10
++
++ \brief The option to stop writing to the device after encountering an error.
++
++ If this property is set to \c true, the writer stops writing immediately upon
++ encountering any error and ignores all subsequent write operations.
++ When this property is set to \c false, the writer may continue writing
++ after an error, skipping the invalid write but allowing further output.
++
++ Note that this includes \l Error::InvalidCharacter, \l Error::Encoding,
++ and \l Error::Custom. \l Error::IO is always considered terminal
++ and stops writing regardless of this setting.
++
++ The default value is \c false.
++ */
++ bool QXmlStreamWriter::stopWritingOnError() const
++ {
++ Q_D(const QXmlStreamWriter);
++ return d->stopWritingOnError;
++ }
++
++ void QXmlStreamWriter::setStopWritingOnError(bool stop)
++ {
++ Q_D(QXmlStreamWriter);
++ d->stopWritingOnError = stop;
++ }
++
++ /*!
++ Returns \c true if an error occurred while trying to write data.
++
++ If the error is \l Error::IO, subsequent writes to the underlying
++ QIODevice will fail. In other cases malformed data might be written to
++ the document.
++
++ The error status is never reset. Writes happening after the error
++ occurred may be ignored, even if the error condition is cleared.
++
++ \sa error(), errorString(), raiseError()
++ */
++ bool QXmlStreamWriter::hasError() const
++ {
++ return error() != QXmlStreamWriter::Error::None;
++ }
++
++ /*!
++ Returns the current error state of the writer.
++
++ If no error has occurred, this function returns
++ QXmlStreamWriter::Error::None.
++
++ \since 6.10
++ \sa errorString(), raiseError(), hasError()
++ */
++ QXmlStreamWriter::Error QXmlStreamWriter::error() const
++ {
++ Q_D(const QXmlStreamWriter);
++ return d->error;
++ }
++
++ /*!
++ If an error has occurred, returns its associated error message.
++
++ The error message is either set internally by QXmlStreamWriter or provided
++ by the user via raiseError(). If no error has occurred, this function returns
++ a null string.
++
++ \since 6.10
++ \sa error(), raiseError(), hasError()
++ */
++ QString QXmlStreamWriter::errorString() const
++ {
++ Q_D(const QXmlStreamWriter);
++ return d->errorString;
++ }
++
++ /*!
++ Raises a custom error with the given \a message.
++
++ This function is for manual indication that an error has occurred during
++ writing, such as an application level validation failure.
++
++ \since 6.10
++ \sa errorString(), error(), hasError()
++ */
++ void QXmlStreamWriter::raiseError(QAnyStringView message)
++ {
++ Q_D(QXmlStreamWriter);
++ d->raiseError(QXmlStreamWriter::Error::Custom, message);
++ }
++
++ /*!
++ \overload
++ Writes an attribute with \a qualifiedName and \a value.
++
++
++ This function can only be called after writeStartElement() before
++ any content is written, or after writeEmptyElement().
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeAttribute(QAnyStringView qualifiedName, QAnyStringView value)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(d->inStartElement);
++ Q_ASSERT(count(qualifiedName, ':') <= 1);
++ d->write(" ");
++ d->write(qualifiedName);
++ d->write("=\"");
++ d->writeEscaped(value, true);
++ d->write("\"");
++ d->didWriteAnyToken = true;
++ }
++
++ /*! Writes an attribute with \a name and \a value, prefixed for
++ the specified \a namespaceUri. If the namespace has not been
++ declared yet, QXmlStreamWriter will generate a namespace declaration
++ for it.
++
++ This function can only be called after writeStartElement() before
++ any content is written, or after writeEmptyElement().
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeAttribute(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView value)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(d->inStartElement);
++ Q_ASSERT(!contains(name, ':'));
++ QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true);
++ d->write(" ");
++ if (!namespaceDeclaration.prefix.isEmpty()) {
++ d->write(namespaceDeclaration.prefix);
++ d->write(":");
++ }
++ d->write(name);
++ d->write("=\"");
++ d->writeEscaped(value, true);
++ d->write("\"");
++ d->didWriteAnyToken = true;
++ }
++
++ /*!
++ \overload
++
++ Writes the \a attribute.
++
++ This function can only be called after writeStartElement() before
++ any content is written, or after writeEmptyElement().
++ */
++ void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute)
++ {
++ if (attribute.namespaceUri().isEmpty())
++ writeAttribute(attribute.qualifiedName(), attribute.value());
++ else
++ writeAttribute(attribute.namespaceUri(), attribute.name(), attribute.value());
++ }
++
++
++ /*! Writes the attribute vector \a attributes. If a namespace
++ referenced in an attribute not been declared yet, QXmlStreamWriter
++ will generate a namespace declaration for it.
++
++ This function can only be called after writeStartElement() before
++ any content is written, or after writeEmptyElement().
++
++ \sa writeAttribute(), writeNamespace()
++ */
++ void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(d->inStartElement);
++ Q_UNUSED(d);
++ for (const auto &attr : attributes)
++ writeAttribute(attr);
++ }
++
++
++ /*! Writes \a text as CDATA section. If \a text contains the
++ forbidden character sequence "]]>", it is split into different CDATA
++ sections.
++
++ 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(QAnyStringView text)
++ {
++ Q_D(QXmlStreamWriter);
++ d->finishStartElement();
++ d->write("<![CDATA[");
++ 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("]]>");
++ }
++
++
++ /*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity
++ references "<", "&, and """. To avoid the forbidden sequence
++ "]]>", ">" is also escaped as ">".
++
++ \sa writeEntityReference()
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeCharacters(QAnyStringView text)
++ {
++ Q_D(QXmlStreamWriter);
++ d->finishStartElement();
++ d->writeEscaped(text);
++ }
++
++
++ /*! Writes \a text as XML comment, where \a text must not contain the
++ forbidden sequence \c{--} or end with \c{-}. Note that XML does not
++ provide any way to escape \c{-} in a comment.
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeComment(QAnyStringView text)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
++ if (!d->finishStartElement(false) && d->autoFormatting)
++ d->indent(d->tagStack.size());
++ d->write("<!--");
++ d->write(text);
++ d->write("-->");
++ d->inStartElement = d->lastWasStartElement = false;
++ }
++
++
++ /*! Writes a DTD section. The \a dtd represents the entire
++ doctypedecl production from the XML 1.0 specification.
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeDTD(QAnyStringView dtd)
++ {
++ Q_D(QXmlStreamWriter);
++ d->finishStartElement();
++ if (d->autoFormatting)
++ d->write("\n");
++ d->write(dtd);
++ if (d->autoFormatting)
++ d->write("\n");
++ }
++
++
++
++ /*! \overload
++ Writes an empty element with qualified name \a qualifiedName.
++ Subsequent calls to writeAttribute() will add attributes to this element.
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeEmptyElement(QAnyStringView qualifiedName)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(count(qualifiedName, ':') <= 1);
++ d->writeStartElement({}, qualifiedName);
++ d->inEmptyElement = true;
++ }
++
++
++ /*! Writes an empty element with \a name, prefixed for the specified
++ \a namespaceUri. If the namespace has not been declared,
++ QXmlStreamWriter will generate a namespace declaration for it.
++ Subsequent calls to writeAttribute() will add attributes to this element.
++
++ \sa writeNamespace()
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeEmptyElement(QAnyStringView namespaceUri, QAnyStringView name)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(!contains(name, ':'));
++ d->writeStartElement(namespaceUri, name);
++ d->inEmptyElement = true;
++ }
++
++
++ /*!\overload
++ Writes a text element with \a qualifiedName and \a text.
++
++
++ This is a convenience function equivalent to:
++ \snippet code/src_corelib_xml_qxmlstream.cpp 1
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeTextElement(QAnyStringView qualifiedName, QAnyStringView text)
++ {
++ writeStartElement(qualifiedName);
++ writeCharacters(text);
++ writeEndElement();
++ }
++
++ /*! Writes a text element with \a name, prefixed for the specified \a
++ namespaceUri, and \a text. If the namespace has not been
++ declared, QXmlStreamWriter will generate a namespace declaration
++ for it.
++
++
++ This is a convenience function equivalent to:
++ \snippet code/src_corelib_xml_qxmlstream.cpp 2
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeTextElement(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView text)
++ {
++ writeStartElement(namespaceUri, name);
++ writeCharacters(text);
++ writeEndElement();
++ }
++
++
++ /*!
++ Closes all remaining open start elements and writes a newline.
++
++ \sa writeStartDocument()
++ */
++ void QXmlStreamWriter::writeEndDocument()
++ {
++ Q_D(QXmlStreamWriter);
++ while (d->tagStack.size())
++ writeEndElement();
++ if (d->didWriteStartDocument || d->didWriteAnyToken)
++ d->write("\n");
++ }
++
++ /*!
++ Closes the previous start element.
++
++ \sa writeStartElement()
++ */
++ void QXmlStreamWriter::writeEndElement()
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(d->didWriteAnyToken);
++ if (d->tagStack.isEmpty())
++ return;
++
++ // shortcut: if nothing was written, close as empty tag
++ if (d->inStartElement && !d->inEmptyElement) {
++ d->write("/>");
++ d->lastWasStartElement = d->inStartElement = false;
++ QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
++ d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
++ return;
++ }
++
++ if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting)
++ d->indent(d->tagStack.size()-1);
++ if (d->tagStack.isEmpty())
++ return;
++ d->lastWasStartElement = false;
++ QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
++ d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
++ d->write("</");
++ if (!tag.namespaceDeclaration.prefix.isEmpty()) {
++ d->write(tag.namespaceDeclaration.prefix);
++ d->write(":");
++ }
++ d->write(tag.name);
++ d->write(">");
++ }
++
++
++
++ /*!
++ Writes the entity reference \a name to the stream, as "&\a{name};".
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeEntityReference(QAnyStringView name)
++ {
++ Q_D(QXmlStreamWriter);
++ d->finishStartElement();
++ d->write("&");
++ d->write(name);
++ d->write(";");
++ }
++
++
++ /*! Writes a namespace declaration for \a namespaceUri with \a
++ prefix. If \a prefix is empty, QXmlStreamWriter assigns a unique
++ prefix consisting of the letter 'n' followed by a number.
++
++ If writeStartElement() or writeEmptyElement() was called, the
++ declaration applies to the current element; otherwise it applies to
++ the next child element.
++
++ Note that the prefix \e xml is both predefined and reserved for
++ \e http://www.w3.org/XML/1998/namespace, which in turn cannot be
++ bound to any other prefix. The prefix \e xmlns and its URI
++ \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism
++ itself and thus completely forbidden in declarations.
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(prefix != "xmlns"_L1);
++ if (prefix.isEmpty()) {
++ d->findNamespace(namespaceUri, d->inStartElement);
++ } else {
++ auto &namespaceDeclaration = d->addExtraNamespace(namespaceUri, prefix);
++ if (d->inStartElement)
++ d->writeNamespaceDeclaration(namespaceDeclaration);
++ }
++ }
++
++
++ /*! Writes a default namespace declaration for \a namespaceUri.
++
++ If writeStartElement() or writeEmptyElement() was called, the
++ declaration applies to the current element; otherwise it applies to
++ the next child element.
++
++ Note that the namespaces \e http://www.w3.org/XML/1998/namespace
++ (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to
++ \e xml) by definition cannot be declared as default.
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeDefaultNamespace(QAnyStringView namespaceUri)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(namespaceUri != "http://www.w3.org/XML/1998/namespace"_L1);
++ Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
++ QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
++ namespaceDeclaration.prefix.clear();
++ namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri);
++ if (d->inStartElement)
++ d->writeNamespaceDeclaration(namespaceDeclaration);
++ }
++
++
++ /*!
++ Writes an XML processing instruction with \a target and \a data,
++ where \a data must not contain the sequence "?>".
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeProcessingInstruction(QAnyStringView target, QAnyStringView data)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(!contains(data, "?>"_L1));
++ if (!d->finishStartElement(false) && d->autoFormatting)
++ d->indent(d->tagStack.size());
++ d->write("<?");
++ d->write(target);
++ if (!data.isNull()) {
++ d->write(" ");
++ d->write(data);
++ }
++ d->write("?>");
++ d->didWriteAnyToken = true;
++ }
++
++
++
++ /*!\overload
++
++ Writes a document start with XML version number "1.0".
++
++ \sa writeEndDocument()
++ \since 4.5
++ */
++ void QXmlStreamWriter::writeStartDocument()
++ {
++ writeStartDocument("1.0"_L1);
++ }
++
++
++ /*!
++ Writes a document start with the XML version number \a version.
++
++ \note This function does not validate the version string and
++ allows setting it manually. However, QXmlStreamWriter only
++ supports XML 1.0. Setting a version string
++ other than "1.0" does not change the writer's behavior or
++ escaping rules. It is the caller's responsibility to ensure
++ consistency between the declared version and the actual content.
++
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++
++ \sa writeEndDocument()
++ */
++ void QXmlStreamWriter::writeStartDocument(QAnyStringView version)
++ {
++ Q_D(QXmlStreamWriter);
++ d->finishStartElement(false);
++ d->write("<?xml version=\"");
++ d->write(version);
++ if (d->device) // stringDevice does not get any encoding
++ d->write("\" encoding=\"UTF-8");
++ d->write("\"?>");
++ d->didWriteStartDocument = true;
++ }
++
++ /*!
++ \since 4.5
++ Writes a document start with the XML version number \a version
++ and a standalone attribute \a standalone.
++
++ \note This function does not validate the version string and
++ allows setting it manually. However, QXmlStreamWriter only
++ supports XML 1.0. Setting a version string
++ other than "1.0" does not change the writer's behavior or
++ escaping rules. It is the caller's responsibility to ensure
++ consistency between the declared version and the actual content.
++
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++
++ \sa writeEndDocument()
++ */
++ void QXmlStreamWriter::writeStartDocument(QAnyStringView version, bool standalone)
++ {
++ Q_D(QXmlStreamWriter);
++ d->finishStartElement(false);
++ d->write("<?xml version=\"");
++ d->write(version);
++ if (d->device) // stringDevice does not get any encoding
++ d->write("\" encoding=\"UTF-8");
++ if (standalone)
++ d->write("\" standalone=\"yes\"?>");
++ else
++ d->write("\" standalone=\"no\"?>");
++ d->didWriteStartDocument = true;
++ }
++
++
++ /*!\overload
++
++ Writes a start element with \a qualifiedName. Subsequent calls to
++ writeAttribute() will add attributes to this element.
++
++ \sa writeEndElement(), writeEmptyElement()
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeStartElement(QAnyStringView qualifiedName)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(count(qualifiedName, ':') <= 1);
++ d->writeStartElement({}, qualifiedName);
++ }
++
++
++ /*! Writes a start element with \a name, prefixed for the specified
++ \a namespaceUri. If the namespace has not been declared yet,
++ QXmlStreamWriter will generate a namespace declaration for
++ it. Subsequent calls to writeAttribute() will add attributes to this
++ element.
++
++ \sa writeNamespace(), writeEndElement(), writeEmptyElement()
++
++ \note In Qt versions prior to 6.5, this function took QString, not
++ QAnyStringView.
++ */
++ void QXmlStreamWriter::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
++ {
++ Q_D(QXmlStreamWriter);
++ Q_ASSERT(!contains(name, ':'));
++ d->writeStartElement(namespaceUri, name);
++ }
++
++ void QXmlStreamWriterPrivate::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
++ StartElementOption option)
++ {
++ if (!finishStartElement(false) && autoFormatting)
++ indent(tagStack.size());
++
++ Tag &tag = tagStack_push();
++ tag.name = addToStringStorage(name);
++ tag.namespaceDeclaration = findNamespace(namespaceUri);
++ write("<");
++ if (!tag.namespaceDeclaration.prefix.isEmpty()) {
++ write(tag.namespaceDeclaration.prefix);
++ write(":");
++ }
++ write(tag.name);
++ inStartElement = lastWasStartElement = true;
++
++ if (option != StartElementOption::OmitNamespaceDeclarations) {
++ for (qsizetype i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i)
++ writeNamespaceDeclaration(namespaceDeclarations[i]);
++ }
++ tag.namespaceDeclarationsSize = lastNamespaceDeclaration;
++ didWriteAnyToken = true;
++ }
++
++ #if QT_CONFIG(xmlstreamreader)
++ /*! Writes the current state of the \a reader. All possible valid
++ states are supported.
++
++ The purpose of this function is to support chained processing of XML data.
++
++ \sa QXmlStreamReader::tokenType()
++ */
++ void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
++ {
++ Q_D(QXmlStreamWriter);
++ switch (reader.tokenType()) {
++ case QXmlStreamReader::NoToken:
++ break;
++ case QXmlStreamReader::StartDocument:
++ writeStartDocument();
++ break;
++ case QXmlStreamReader::EndDocument:
++ writeEndDocument();
++ break;
++ case QXmlStreamReader::StartElement: {
++ // Namespaces must be added before writeStartElement is called so new prefixes are found
++ QList<QXmlStreamPrivateTagStack::NamespaceDeclaration> extraNamespaces;
++ const QXmlStreamNamespaceDeclarations nsDeclarations = reader.namespaceDeclarations();
++ for (const auto &namespaceDeclaration : nsDeclarations) {
++ auto &extraNamespace = d->addExtraNamespace(namespaceDeclaration.namespaceUri(),
++ namespaceDeclaration.prefix());
++ extraNamespaces.append(extraNamespace);
++ }
++ d->writeStartElement(
++ reader.namespaceUri(), reader.name(),
++ QXmlStreamWriterPrivate::StartElementOption::OmitNamespaceDeclarations);
++ // Namespace declarations are written afterwards
++ for (const auto &extraNamespace : std::as_const(extraNamespaces))
++ d->writeNamespaceDeclaration(extraNamespace);
++ writeAttributes(reader.attributes());
++ } break;
++ case QXmlStreamReader::EndElement:
++ writeEndElement();
++ break;
++ case QXmlStreamReader::Characters:
++ if (reader.isCDATA())
++ writeCDATA(reader.text());
++ else
++ writeCharacters(reader.text());
++ break;
++ case QXmlStreamReader::Comment:
++ writeComment(reader.text());
++ break;
++ case QXmlStreamReader::DTD:
++ writeDTD(reader.text());
++ break;
++ case QXmlStreamReader::EntityReference:
++ writeEntityReference(reader.name());
++ break;
++ case QXmlStreamReader::ProcessingInstruction:
++ writeProcessingInstruction(reader.processingInstructionTarget(),
++ reader.processingInstructionData());
++ break;
++ default:
++ Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid);
++ qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state.");
++ break;
++ }
++ }
++ #endif // feature xmlstreamreader
++ #endif // feature xmlstreamwriter
++
++ #if QT_CONFIG(xmlstreamreader)
++ static constexpr bool isTokenAllowedInContext(QXmlStreamReader::TokenType type,
++ QXmlStreamReaderPrivate::XmlContext ctxt)
++ {
++ switch (type) {
++ case QXmlStreamReader::StartDocument:
++ case QXmlStreamReader::DTD:
++ return ctxt == QXmlStreamReaderPrivate::XmlContext::Prolog;
++
++ case QXmlStreamReader::StartElement:
++ case QXmlStreamReader::EndElement:
++ case QXmlStreamReader::Characters:
++ case QXmlStreamReader::EntityReference:
++ case QXmlStreamReader::EndDocument:
++ return ctxt == QXmlStreamReaderPrivate::XmlContext::Body;
++
++ case QXmlStreamReader::Comment:
++ case QXmlStreamReader::ProcessingInstruction:
++ return true;
++
++ case QXmlStreamReader::NoToken:
++ case QXmlStreamReader::Invalid:
++ return false;
++ }
++
++ // GCC 8.x does not treat __builtin_unreachable() as constexpr
++ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
++ Q_UNREACHABLE_RETURN(false);
++ #else
++ return false;
++ #endif
++ }
++
++ /*!
++ \internal
++ \brief QXmlStreamReader::isValidToken
++ \return \c true if \param type is a valid token type.
++ \return \c false if \param type is an unexpected token,
++ which indicates a non-well-formed or invalid XML stream.
++ */
++ bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type)
++ {
++ // Don't change currentContext, if Invalid or NoToken occur in the prolog
++ if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken)
++ return false;
++
++ // If a token type gets rejected in the body, there is no recovery
++ const bool result = isTokenAllowedInContext(type, currentContext);
++ if (result || currentContext == XmlContext::Body)
++ return result;
++
++ // First non-Prolog token observed => switch context to body and check again.
++ currentContext = XmlContext::Body;
++ return isTokenAllowedInContext(type, currentContext);
++ }
++
++ /*!
++ \internal
++ Checks token type and raises an error, if it is invalid
++ in the current context (prolog/body).
++ */
++ void QXmlStreamReaderPrivate::checkToken()
++ {
++ Q_Q(QXmlStreamReader);
++
++ // The token type must be consumed, to keep track if the body has been reached.
++ const XmlContext context = currentContext;
++ const bool ok = isValidToken(type);
++
++ // Do nothing if an error has been raised already (going along with an unexpected token)
++ if (error != QXmlStreamReader::Error::NoError)
++ return;
++
++ if (!ok) {
++ raiseError(QXmlStreamReader::UnexpectedElementError,
++ QXmlStream::tr("Unexpected token type %1 in %2.")
++ .arg(q->tokenString(), contextString(context)));
++ return;
++ }
++
++ if (type != QXmlStreamReader::DTD)
++ return;
++
++ // Raise error on multiple DTD tokens
++ if (foundDTD) {
++ raiseError(QXmlStreamReader::UnexpectedElementError,
++ QXmlStream::tr("Found second DTD token in %1.").arg(contextString(context)));
++ } else {
++ foundDTD = true;
++ }
++ }
++
++ /*!
++ \fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView qualifiedName) const
++
++ Returns \c true if this QXmlStreamAttributes has an attribute whose
++ qualified name is \a qualifiedName; otherwise returns \c false.
++
++ Note that this is not namespace aware. For instance, if this
++ QXmlStreamAttributes contains an attribute whose lexical name is "xlink:href"
++ this doesn't tell that an attribute named \c href in the XLink namespace is
++ present, since the \c xlink prefix can be bound to any namespace. Use the
++ overload that takes a namespace URI and a local name as parameter, for
++ namespace aware code.
++ */
++
++ /*!
++ \fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView namespaceUri,
++ QAnyStringView name) const
++ \overload
++
++ Returns \c true if this QXmlStreamAttributes has an attribute whose
++ namespace URI and name correspond to \a namespaceUri and \a name;
++ otherwise returns \c false.
++ */
++
++ #endif // feature xmlstreamreader
++
++ QT_END_NAMESPACE
++
++ #endif // feature xmlstream