diff options
Diffstat (limited to 'src/corelib/serialization')
29 files changed, 22343 insertions, 0 deletions
diff --git a/src/corelib/serialization/.gitignore b/src/corelib/serialization/.gitignore new file mode 100644 index 0000000000..89f9ac04aa --- /dev/null +++ b/src/corelib/serialization/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/src/corelib/serialization/make-xml-parser.sh b/src/corelib/serialization/make-xml-parser.sh new file mode 100755 index 0000000000..0296e4c22b --- /dev/null +++ b/src/corelib/serialization/make-xml-parser.sh @@ -0,0 +1,51 @@ +#!/bin/sh +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +me=$(dirname $0) +mkdir -p $me/out +(cd $me/out && ../../../../util/qlalr/qlalr --qt --no-debug --no-lines ../qxmlstream.g) + +for f in $me/out/*.h; do + n=$(basename $f) + cp $f $n +done + +git diff . + diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp new file mode 100644 index 0000000000..8f419a4a46 --- /dev/null +++ b/src/corelib/serialization/qdatastream.cpp @@ -0,0 +1,1400 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdatastream.h" +#include "qdatastream_p.h" + +#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) +#include "qbuffer.h" +#include "qfloat16.h" +#include "qstring.h" +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include "qendian.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDataStream + \inmodule QtCore + \reentrant + \brief The QDataStream class provides serialization of binary data + to a QIODevice. + + \ingroup io + + + A data stream is a binary stream of encoded information which is + 100% independent of the host computer's operating system, CPU or + byte order. For example, a data stream that is written by a PC + under Windows can be read by a Sun SPARC running Solaris. + + You can also use a data stream to read/write \l{raw}{raw + unencoded binary data}. If you want a "parsing" input stream, see + QTextStream. + + The QDataStream class implements the serialization of C++'s basic + data types, like \c char, \c short, \c int, \c{char *}, etc. + Serialization of more complex data is accomplished by breaking up + the data into primitive units. + + A data stream cooperates closely with a QIODevice. A QIODevice + represents an input/output medium one can read data from and write + data to. The QFile class is an example of an I/O device. + + Example (write binary data to a stream): + + \snippet code/src_corelib_io_qdatastream.cpp 0 + + Example (read binary data from a stream): + + \snippet code/src_corelib_io_qdatastream.cpp 1 + + Each item written to the stream is written in a predefined binary + format that varies depending on the item's type. Supported Qt + types include QBrush, QColor, QDateTime, QFont, QPixmap, QString, + QVariant and many others. For the complete list of all Qt types + supporting data streaming see \l{Serializing Qt Data Types}. + + For integers it is best to always cast to a Qt integer type for + writing, and to read back into the same Qt integer type. This + ensures that you get integers of the size you want and insulates + you from compiler and platform differences. + + To take one example, a \c{char *} string is written as a 32-bit + integer equal to the length of the string including the '\\0' byte, + followed by all the characters of the string including the + '\\0' byte. When reading a \c{char *} string, 4 bytes are read to + create the 32-bit length value, then that many characters for the + \c {char *} string including the '\\0' terminator are read. + + The initial I/O device is usually set in the constructor, but can be + changed with setDevice(). If you've reached the end of the data + (or if there is no I/O device set) atEnd() will return true. + + \section1 Versioning + + QDataStream's binary format has evolved since Qt 1.0, and is + likely to continue evolving to reflect changes done in Qt. When + inputting or outputting complex types, it's very important to + make sure that the same version of the stream (version()) is used + for reading and writing. If you need both forward and backward + compatibility, you can hardcode the version number in the + application: + + \snippet code/src_corelib_io_qdatastream.cpp 2 + + If you are producing a new binary data format, such as a file + format for documents created by your application, you could use a + QDataStream to write the data in a portable format. Typically, you + would write a brief header containing a magic string and a version + number to give yourself room for future expansion. For example: + + \snippet code/src_corelib_io_qdatastream.cpp 3 + + Then read it in with: + + \snippet code/src_corelib_io_qdatastream.cpp 4 + + You can select which byte order to use when serializing data. The + default setting is big endian (MSB first). Changing it to little + endian breaks the portability (unless the reader also changes to + little endian). We recommend keeping this setting unless you have + special requirements. + + \target raw + \section1 Reading and Writing Raw Binary Data + + You may wish to read/write your own raw binary data to/from the + data stream directly. Data may be read from the stream into a + preallocated \c{char *} using readRawData(). Similarly data can be + written to the stream using writeRawData(). Note that any + encoding/decoding of the data must be done by you. + + A similar pair of functions is readBytes() and writeBytes(). These + differ from their \e raw counterparts as follows: readBytes() + reads a quint32 which is taken to be the length of the data to be + read, then that number of bytes is read into the preallocated + \c{char *}; writeBytes() writes a quint32 containing the length of the + data, followed by the data. Note that any encoding/decoding of + the data (apart from the length quint32) must be done by you. + + \section1 Reading and Writing Qt Collection Classes + + The Qt container classes can also be serialized to a QDataStream. + These include QList, QLinkedList, QVector, QSet, QHash, and QMap. + The stream operators are declared as non-members of the classes. + + \target Serializing Qt Classes + \section1 Reading and Writing Other Qt Classes + + In addition to the overloaded stream operators documented here, + any Qt classes that you might want to serialize to a QDataStream + will have appropriate stream operators declared as non-member of + the class: + + \code + QDataStream &operator<<(QDataStream &, const QXxx &); + QDataStream &operator>>(QDataStream &, QXxx &); + \endcode + + For example, here are the stream operators declared as non-members + of the QImage class: + + \code + QDataStream & operator<< (QDataStream& stream, const QImage& image); + QDataStream & operator>> (QDataStream& stream, QImage& image); + \endcode + + To see if your favorite Qt class has similar stream operators + defined, check the \b {Related Non-Members} section of the + class's documentation page. + + \section1 Using Read Transactions + + When a data stream operates on an asynchronous device, the chunks of data + can arrive at arbitrary points in time. The QDataStream class implements + a transaction mechanism that provides the ability to read the data + atomically with a series of stream operators. As an example, you can + handle incomplete reads from a socket by using a transaction in a slot + connected to the readyRead() signal: + + \snippet code/src_corelib_io_qdatastream.cpp 6 + + If no full packet is received, this code restores the stream to the + initial position, after which you need to wait for more data to arrive. + + \sa QTextStream, QVariant +*/ + +/*! + \enum QDataStream::ByteOrder + + The byte order used for reading/writing the data. + + \value BigEndian Most significant byte first (the default) + \value LittleEndian Least significant byte first +*/ + +/*! + \enum QDataStream::FloatingPointPrecision + + The precision of floating point numbers used for reading/writing the data. This will only have + an effect if the version of the data stream is Qt_4_6 or higher. + + \warning The floating point precision must be set to the same value on the object that writes + and the object that reads the data stream. + + \value SinglePrecision All floating point numbers in the data stream have 32-bit precision. + \value DoublePrecision All floating point numbers in the data stream have 64-bit precision. + + \sa setFloatingPointPrecision(), floatingPointPrecision() +*/ + +/*! + \enum QDataStream::Status + + This enum describes the current status of the data stream. + + \value Ok The data stream is operating normally. + \value ReadPastEnd The data stream has read past the end of the + data in the underlying device. + \value ReadCorruptData The data stream has read corrupt data. + \value WriteFailed The data stream cannot write to the underlying device. +*/ + +/***************************************************************************** + QDataStream member functions + *****************************************************************************/ + +#define Q_VOID + +#undef CHECK_STREAM_PRECOND +#ifndef QT_NO_DEBUG +#define CHECK_STREAM_PRECOND(retVal) \ + if (!dev) { \ + qWarning("QDataStream: No device"); \ + return retVal; \ + } +#else +#define CHECK_STREAM_PRECOND(retVal) \ + if (!dev) { \ + return retVal; \ + } +#endif + +#define CHECK_STREAM_WRITE_PRECOND(retVal) \ + CHECK_STREAM_PRECOND(retVal) \ + if (q_status != Ok) \ + return retVal; + +#define CHECK_STREAM_TRANSACTION_PRECOND(retVal) \ + if (!d || d->transactionDepth == 0) { \ + qWarning("QDataStream: No transaction in progress"); \ + return retVal; \ + } + +/*! + Constructs a data stream that has no I/O device. + + \sa setDevice() +*/ + +QDataStream::QDataStream() +{ + dev = 0; + owndev = false; + byteorder = BigEndian; + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + Constructs a data stream that uses the I/O device \a d. + + \sa setDevice(), device() +*/ + +QDataStream::QDataStream(QIODevice *d) +{ + dev = d; // set device + owndev = false; + byteorder = BigEndian; // default byte order + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + \fn QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode mode) + + Constructs a data stream that operates on a byte array, \a a. The + \a mode describes how the device is to be used. + + Alternatively, you can use QDataStream(const QByteArray &) if you + just want to read from a byte array. + + Since QByteArray is not a QIODevice subclass, internally a QBuffer + is created to wrap the byte array. +*/ + +QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode flags) +{ + QBuffer *buf = new QBuffer(a); +#ifndef QT_NO_QOBJECT + buf->blockSignals(true); +#endif + buf->open(flags); + dev = buf; + owndev = true; + byteorder = BigEndian; + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + Constructs a read-only data stream that operates on byte array \a a. + Use QDataStream(QByteArray*, int) if you want to write to a byte + array. + + Since QByteArray is not a QIODevice subclass, internally a QBuffer + is created to wrap the byte array. +*/ +QDataStream::QDataStream(const QByteArray &a) +{ + QBuffer *buf = new QBuffer; +#ifndef QT_NO_QOBJECT + buf->blockSignals(true); +#endif + buf->setData(a); + buf->open(QIODevice::ReadOnly); + dev = buf; + owndev = true; + byteorder = BigEndian; + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + Destroys the data stream. + + The destructor will not affect the current I/O device, unless it is + an internal I/O device (e.g. a QBuffer) processing a QByteArray + passed in the \e constructor, in which case the internal I/O device + is destroyed. +*/ + +QDataStream::~QDataStream() +{ + if (owndev) + delete dev; +} + + +/*! + \fn QIODevice *QDataStream::device() const + + Returns the I/O device currently set, or 0 if no + device is currently set. + + \sa setDevice() +*/ + +/*! + void QDataStream::setDevice(QIODevice *d) + + Sets the I/O device to \a d, which can be 0 + to unset to current I/O device. + + \sa device() +*/ + +void QDataStream::setDevice(QIODevice *d) +{ + if (owndev) { + delete dev; + owndev = false; + } + dev = d; +} + +/*! + \obsolete + Unsets the I/O device. + Use setDevice(0) instead. +*/ + +void QDataStream::unsetDevice() +{ + setDevice(0); +} + + +/*! + \fn bool QDataStream::atEnd() const + + Returns \c true if the I/O device has reached the end position (end of + the stream or file) or if there is no I/O device set; otherwise + returns \c false. + + \sa QIODevice::atEnd() +*/ + +bool QDataStream::atEnd() const +{ + return dev ? dev->atEnd() : true; +} + +/*! + Returns the floating point precision of the data stream. + + \since 4.6 + + \sa FloatingPointPrecision, setFloatingPointPrecision() +*/ +QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const +{ + return d == 0 ? QDataStream::DoublePrecision : d->floatingPointPrecision; +} + +/*! + Sets the floating point precision of the data stream to \a precision. If the floating point precision is + DoublePrecision and the version of the data stream is Qt_4_6 or higher, all floating point + numbers will be written and read with 64-bit precision. If the floating point precision is + SinglePrecision and the version is Qt_4_6 or higher, all floating point numbers will be written + and read with 32-bit precision. + + For versions prior to Qt_4_6, the precision of floating point numbers in the data stream depends + on the stream operator called. + + The default is DoublePrecision. + + Note that this property does not affect the serialization or deserialization of \c qfloat16 + instances. + + \warning This property must be set to the same value on the object that writes and the object + that reads the data stream. + + \since 4.6 +*/ +void QDataStream::setFloatingPointPrecision(QDataStream::FloatingPointPrecision precision) +{ + if (d == 0) + d.reset(new QDataStreamPrivate()); + d->floatingPointPrecision = precision; +} + +/*! + Returns the status of the data stream. + + \sa Status, setStatus(), resetStatus() +*/ + +QDataStream::Status QDataStream::status() const +{ + return q_status; +} + +/*! + Resets the status of the data stream. + + \sa Status, status(), setStatus() +*/ +void QDataStream::resetStatus() +{ + q_status = Ok; +} + +/*! + Sets the status of the data stream to the \a status given. + + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + + \sa Status, status(), resetStatus() +*/ +void QDataStream::setStatus(Status status) +{ + if (q_status == Ok) + q_status = status; +} + +/*! + \fn int QDataStream::byteOrder() const + + Returns the current byte order setting -- either BigEndian or + LittleEndian. + + \sa setByteOrder() +*/ + +/*! + Sets the serialization byte order to \a bo. + + The \a bo parameter can be QDataStream::BigEndian or + QDataStream::LittleEndian. + + The default setting is big endian. We recommend leaving this + setting unless you have special requirements. + + \sa byteOrder() +*/ + +void QDataStream::setByteOrder(ByteOrder bo) +{ + byteorder = bo; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + noswap = (byteorder == BigEndian); + else + noswap = (byteorder == LittleEndian); +} + + +/*! + \enum QDataStream::Version + + This enum provides symbolic synonyms for the data serialization + format version numbers. + + \value Qt_1_0 Version 1 (Qt 1.x) + \value Qt_2_0 Version 2 (Qt 2.0) + \value Qt_2_1 Version 3 (Qt 2.1, 2.2, 2.3) + \value Qt_3_0 Version 4 (Qt 3.0) + \value Qt_3_1 Version 5 (Qt 3.1, 3.2) + \value Qt_3_3 Version 6 (Qt 3.3) + \value Qt_4_0 Version 7 (Qt 4.0, Qt 4.1) + \value Qt_4_1 Version 7 (Qt 4.0, Qt 4.1) + \value Qt_4_2 Version 8 (Qt 4.2) + \value Qt_4_3 Version 9 (Qt 4.3) + \value Qt_4_4 Version 10 (Qt 4.4) + \value Qt_4_5 Version 11 (Qt 4.5) + \value Qt_4_6 Version 12 (Qt 4.6, Qt 4.7, Qt 4.8) + \value Qt_4_7 Same as Qt_4_6. + \value Qt_4_8 Same as Qt_4_6. + \value Qt_4_9 Same as Qt_4_6. + \value Qt_5_0 Version 13 (Qt 5.0) + \value Qt_5_1 Version 14 (Qt 5.1) + \value Qt_5_2 Version 15 (Qt 5.2) + \value Qt_5_3 Same as Qt_5_2 + \value Qt_5_4 Version 16 (Qt 5.4) + \value Qt_5_5 Same as Qt_5_4 + \value Qt_5_6 Version 17 (Qt 5.6) + \value Qt_5_7 Same as Qt_5_6 + \value Qt_5_8 Same as Qt_5_6 + \value Qt_5_9 Same as Qt_5_6 + \value Qt_5_10 Same as Qt_5_6 + \value Qt_5_11 Same as Qt_5_6 + \omitvalue Qt_DefaultCompiledVersion + + \sa setVersion(), version() +*/ + +/*! + \fn int QDataStream::version() const + + Returns the version number of the data serialization format. + + \sa setVersion(), Version +*/ + +/*! + \fn void QDataStream::setVersion(int v) + + Sets the version number of the data serialization format to \a v, + a value of the \l Version enum. + + You don't \e have to set a version if you are using the current + version of Qt, but for your own custom binary formats we + recommend that you do; see \l{Versioning} in the Detailed + Description. + + To accommodate new functionality, the datastream serialization + format of some Qt classes has changed in some versions of Qt. If + you want to read data that was created by an earlier version of + Qt, or write data that can be read by a program that was compiled + with an earlier version of Qt, use this function to modify the + serialization format used by QDataStream. + + The \l Version enum provides symbolic constants for the different + versions of Qt. For example: + + \snippet code/src_corelib_io_qdatastream.cpp 5 + + \sa version(), Version +*/ + +/*! + \since 5.7 + + Starts a new read transaction on the stream. + + Defines a restorable point within the sequence of read operations. For + sequential devices, read data will be duplicated internally to allow + recovery in case of incomplete reads. For random-access devices, + this function saves the current position of the stream. Call + commitTransaction(), rollbackTransaction(), or abortTransaction() to + finish the current transaction. + + Once a transaction is started, subsequent calls to this function will make + the transaction recursive. Inner transactions act as agents of the + outermost transaction (i.e., report the status of read operations to the + outermost transaction, which can restore the position of the stream). + + \note Restoring to the point of the nested startTransaction() call is not + supported. + + When an error occurs during a transaction (including an inner transaction + failing), reading from the data stream is suspended (all subsequent read + operations return empty/zero values) and subsequent inner transactions are + forced to fail. Starting a new outermost transaction recovers from this + state. This behavior makes it unnecessary to error-check every read + operation separately. + + \sa commitTransaction(), rollbackTransaction(), abortTransaction() +*/ + +void QDataStream::startTransaction() +{ + CHECK_STREAM_PRECOND(Q_VOID) + + if (d == 0) + d.reset(new QDataStreamPrivate()); + + if (++d->transactionDepth == 1) { + dev->startTransaction(); + resetStatus(); + } +} + +/*! + \since 5.7 + + Completes a read transaction. Returns \c true if no read errors have + occurred during the transaction; otherwise returns \c false. + + If called on an inner transaction, committing will be postponed until + the outermost commitTransaction(), rollbackTransaction(), or + abortTransaction() call occurs. + + Otherwise, if the stream status indicates reading past the end of the + data, this function restores the stream data to the point of the + startTransaction() call. When this situation occurs, you need to wait for + more data to arrive, after which you start a new transaction. If the data + stream has read corrupt data or any of the inner transactions was aborted, + this function aborts the transaction. + + \sa startTransaction(), rollbackTransaction(), abortTransaction() +*/ + +bool QDataStream::commitTransaction() +{ + CHECK_STREAM_TRANSACTION_PRECOND(false) + if (--d->transactionDepth == 0) { + CHECK_STREAM_PRECOND(false) + + if (q_status == ReadPastEnd) { + dev->rollbackTransaction(); + return false; + } + dev->commitTransaction(); + } + return q_status == Ok; +} + +/*! + \since 5.7 + + Reverts a read transaction. + + This function is commonly used to rollback the transaction when an + incomplete read was detected prior to committing the transaction. + + If called on an inner transaction, reverting is delegated to the outermost + transaction, and subsequently started inner transactions are forced to + fail. + + For the outermost transaction, restores the stream data to the point of + the startTransaction() call. If the data stream has read corrupt data or + any of the inner transactions was aborted, this function aborts the + transaction. + + If the preceding stream operations were successful, sets the status of the + data stream to \value ReadPastEnd. + + \sa startTransaction(), commitTransaction(), abortTransaction() +*/ + +void QDataStream::rollbackTransaction() +{ + setStatus(ReadPastEnd); + + CHECK_STREAM_TRANSACTION_PRECOND(Q_VOID) + if (--d->transactionDepth != 0) + return; + + CHECK_STREAM_PRECOND(Q_VOID) + if (q_status == ReadPastEnd) + dev->rollbackTransaction(); + else + dev->commitTransaction(); +} + +/*! + \since 5.7 + + Aborts a read transaction. + + This function is commonly used to discard the transaction after + higher-level protocol errors or loss of stream synchronization. + + If called on an inner transaction, aborting is delegated to the outermost + transaction, and subsequently started inner transactions are forced to + fail. + + For the outermost transaction, discards the restoration point and any + internally duplicated data of the stream. Will not affect the current + read position of the stream. + + Sets the status of the data stream to \value ReadCorruptData. + + \sa startTransaction(), commitTransaction(), rollbackTransaction() +*/ + +void QDataStream::abortTransaction() +{ + q_status = ReadCorruptData; + + CHECK_STREAM_TRANSACTION_PRECOND(Q_VOID) + if (--d->transactionDepth != 0) + return; + + CHECK_STREAM_PRECOND(Q_VOID) + dev->commitTransaction(); +} + +/***************************************************************************** + QDataStream read functions + *****************************************************************************/ + +/*! + \internal +*/ + +int QDataStream::readBlock(char *data, int len) +{ + // Disable reads on failure in transacted stream + if (q_status != Ok && dev->isTransactionStarted()) + return -1; + + const int readResult = dev->read(data, len); + if (readResult != len) + setStatus(ReadPastEnd); + return readResult; +} + +/*! + \fn QDataStream &QDataStream::operator>>(std::nullptr_t &ptr) + \since 5.9 + \overload + + Simulates reading a \c{std::nullptr_t} from the stream into \a ptr and + returns a reference to the stream. This function does not actually read + anything from the stream, as \c{std::nullptr_t} values are stored as 0 + bytes. +*/ + +/*! + \fn QDataStream &QDataStream::operator>>(quint8 &i) + \overload + + Reads an unsigned byte from the stream into \a i, and returns a + reference to the stream. +*/ + +/*! + Reads a signed byte from the stream into \a i, and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint8 &i) +{ + i = 0; + CHECK_STREAM_PRECOND(*this) + char c; + if (readBlock(&c, 1) == 1) + i = qint8(c); + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator>>(quint16 &i) + \overload + + Reads an unsigned 16-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +/*! + \overload + + Reads a signed 16-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint16 &i) +{ + i = 0; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast<char *>(&i), 2) != 2) { + i = 0; + } else { + if (!noswap) { + i = qbswap(i); + } + } + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator>>(quint32 &i) + \overload + + Reads an unsigned 32-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +/*! + \overload + + Reads a signed 32-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint32 &i) +{ + i = 0; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast<char *>(&i), 4) != 4) { + i = 0; + } else { + if (!noswap) { + i = qbswap(i); + } + } + return *this; +} + +/*! + \fn QDataStream &QDataStream::operator>>(quint64 &i) + \overload + + Reads an unsigned 64-bit integer from the stream, into \a i, and + returns a reference to the stream. +*/ + +/*! + \overload + + Reads a signed 64-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint64 &i) +{ + i = qint64(0); + CHECK_STREAM_PRECOND(*this) + if (version() < 6) { + quint32 i1, i2; + *this >> i2 >> i1; + i = ((quint64)i1 << 32) + i2; + } else { + if (readBlock(reinterpret_cast<char *>(&i), 8) != 8) { + i = qint64(0); + } else { + if (!noswap) { + i = qbswap(i); + } + } + } + return *this; +} + +/*! + Reads a boolean value from the stream into \a i. Returns a + reference to the stream. +*/ +QDataStream &QDataStream::operator>>(bool &i) +{ + qint8 v; + *this >> v; + i = !!v; + return *this; +} + +/*! + \overload + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator>>(float &f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::DoublePrecision) { + double d; + *this >> d; + f = d; + return *this; + } + + f = 0.0f; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast<char *>(&f), 4) != 4) { + f = 0.0f; + } else { + if (!noswap) { + union { + float val1; + quint32 val2; + } x; + x.val2 = qbswap(*reinterpret_cast<quint32 *>(&f)); + f = x.val1; + } + } + return *this; +} + +/*! + \overload + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator>>(double &f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::SinglePrecision) { + float d; + *this >> d; + f = d; + return *this; + } + + f = 0.0; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast<char *>(&f), 8) != 8) { + f = 0.0; + } else { + if (!noswap) { + union { + double val1; + quint64 val2; + } x; + x.val2 = qbswap(*reinterpret_cast<quint64 *>(&f)); + f = x.val1; + } + } + return *this; +} + + +/*! + \overload + \since 5.9 + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. +*/ +QDataStream &QDataStream::operator>>(qfloat16 &f) +{ + return *this >> reinterpret_cast<qint16&>(f); +} + + +/*! + \overload + + Reads the '\\0'-terminated string \a s from the stream and returns + a reference to the stream. + + The string is deserialized using \c{readBytes()}. + + Space for the string is allocated using \c{new []} -- the caller must + destroy it with \c{delete []}. + + \sa readBytes(), readRawData() +*/ + +QDataStream &QDataStream::operator>>(char *&s) +{ + uint len = 0; + return readBytes(s, len); +} + + +/*! + Reads the buffer \a s from the stream and returns a reference to + the stream. + + The buffer \a s is allocated using \c{new []}. Destroy it with the + \c{delete []} operator. + + The \a l parameter is set to the length of the buffer. If the + string read is empty, \a l is set to 0 and \a s is set to + a null pointer. + + The serialization format is a quint32 length specifier first, + then \a l bytes of data. + + \sa readRawData(), writeBytes() +*/ + +QDataStream &QDataStream::readBytes(char *&s, uint &l) +{ + s = 0; + l = 0; + CHECK_STREAM_PRECOND(*this) + + quint32 len; + *this >> len; + if (len == 0) + return *this; + + const quint32 Step = 1024 * 1024; + quint32 allocated = 0; + char *prevBuf = 0; + char *curBuf = 0; + + do { + int blockSize = qMin(Step, len - allocated); + prevBuf = curBuf; + curBuf = new char[allocated + blockSize + 1]; + if (prevBuf) { + memcpy(curBuf, prevBuf, allocated); + delete [] prevBuf; + } + if (readBlock(curBuf + allocated, blockSize) != blockSize) { + delete [] curBuf; + return *this; + } + allocated += blockSize; + } while (allocated < len); + + s = curBuf; + s[len] = '\0'; + l = (uint)len; + return *this; +} + +/*! + Reads at most \a len bytes from the stream into \a s and returns the number of + bytes read. If an error occurs, this function returns -1. + + The buffer \a s must be preallocated. The data is \e not encoded. + + \sa readBytes(), QIODevice::read(), writeRawData() +*/ + +int QDataStream::readRawData(char *s, int len) +{ + CHECK_STREAM_PRECOND(-1) + return readBlock(s, len); +} + + +/***************************************************************************** + QDataStream write functions + *****************************************************************************/ + +/*! + \fn QDataStream &QDataStream::operator<<(std::nullptr_t ptr) + \since 5.9 + \overload + + Simulates writing a \c{std::nullptr_t}, \a ptr, to the stream and returns a + reference to the stream. This function does not actually write anything to + the stream, as \c{std::nullptr_t} values are stored as 0 bytes. +*/ + +/*! + \fn QDataStream &QDataStream::operator<<(quint8 i) + \overload + + Writes an unsigned byte, \a i, to the stream and returns a + reference to the stream. +*/ + +/*! + Writes a signed byte, \a i, to the stream and returns a reference + to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint8 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(i)) + q_status = WriteFailed; + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator<<(quint16 i) + \overload + + Writes an unsigned 16-bit integer, \a i, to the stream and returns + a reference to the stream. +*/ + +/*! + \overload + + Writes a signed 16-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint16 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!noswap) { + i = qbswap(i); + } + if (dev->write((char *)&i, sizeof(qint16)) != sizeof(qint16)) + q_status = WriteFailed; + return *this; +} + +/*! + \overload + + Writes a signed 32-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint32 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!noswap) { + i = qbswap(i); + } + if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32)) + q_status = WriteFailed; + return *this; +} + +/*! + \fn QDataStream &QDataStream::operator<<(quint64 i) + \overload + + Writes an unsigned 64-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +/*! + \overload + + Writes a signed 64-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint64 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (version() < 6) { + quint32 i1 = i & 0xffffffff; + quint32 i2 = i >> 32; + *this << i2 << i1; + } else { + if (!noswap) { + i = qbswap(i); + } + if (dev->write((char *)&i, sizeof(qint64)) != sizeof(qint64)) + q_status = WriteFailed; + } + return *this; +} + +/*! + \fn QDataStream &QDataStream::operator<<(quint32 i) + \overload + + Writes an unsigned integer, \a i, to the stream as a 32-bit + unsigned integer (quint32). Returns a reference to the stream. +*/ + +/*! + Writes a boolean value, \a i, to the stream. Returns a reference + to the stream. +*/ + +QDataStream &QDataStream::operator<<(bool i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(qint8(i))) + q_status = WriteFailed; + return *this; +} + +/*! + \overload + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator<<(float f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::DoublePrecision) { + *this << double(f); + return *this; + } + + CHECK_STREAM_WRITE_PRECOND(*this) + float g = f; // fixes float-on-stack problem + if (!noswap) { + union { + float val1; + quint32 val2; + } x; + x.val1 = g; + x.val2 = qbswap(x.val2); + + if (dev->write((char *)&x.val2, sizeof(float)) != sizeof(float)) + q_status = WriteFailed; + return *this; + } + + if (dev->write((char *)&g, sizeof(float)) != sizeof(float)) + q_status = WriteFailed; + return *this; +} + + +/*! + \overload + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator<<(double f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::SinglePrecision) { + *this << float(f); + return *this; + } + + CHECK_STREAM_WRITE_PRECOND(*this) + if (noswap) { + if (dev->write((char *)&f, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; + } else { + union { + double val1; + quint64 val2; + } x; + x.val1 = f; + x.val2 = qbswap(x.val2); + if (dev->write((char *)&x.val2, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; + } + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator<<(qfloat16 f) + \overload + \since 5.9 + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. +*/ +QDataStream &QDataStream::operator<<(qfloat16 f) +{ + return *this << reinterpret_cast<qint16&>(f); +} + +/*! + \overload + + Writes the '\\0'-terminated string \a s to the stream and returns a + reference to the stream. + + The string is serialized using \c{writeBytes()}. + + \sa writeBytes(), writeRawData() +*/ + +QDataStream &QDataStream::operator<<(const char *s) +{ + if (!s) { + *this << (quint32)0; + return *this; + } + uint len = qstrlen(s) + 1; // also write null terminator + *this << (quint32)len; // write length specifier + writeRawData(s, len); + return *this; +} + +/*! + Writes the length specifier \a len and the buffer \a s to the + stream and returns a reference to the stream. + + The \a len is serialized as a quint32, followed by \a len bytes + from \a s. Note that the data is \e not encoded. + + \sa writeRawData(), readBytes() +*/ + +QDataStream &QDataStream::writeBytes(const char *s, uint len) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + *this << (quint32)len; // write length specifier + if (len) + writeRawData(s, len); + return *this; +} + + +/*! + Writes \a len bytes from \a s to the stream. Returns the + number of bytes actually written, or -1 on error. + The data is \e not encoded. + + \sa writeBytes(), QIODevice::write(), readRawData() +*/ + +int QDataStream::writeRawData(const char *s, int len) +{ + CHECK_STREAM_WRITE_PRECOND(-1) + int ret = dev->write(s, len); + if (ret != len) + q_status = WriteFailed; + return ret; +} + +/*! + \since 4.1 + + Skips \a len bytes from the device. Returns the number of bytes + actually skipped, or -1 on error. + + This is equivalent to calling readRawData() on a buffer of length + \a len and ignoring the buffer. + + \sa QIODevice::seek() +*/ +int QDataStream::skipRawData(int len) +{ + CHECK_STREAM_PRECOND(-1) + if (q_status != Ok && dev->isTransactionStarted()) + return -1; + + const int skipResult = dev->skip(len); + if (skipResult != len) + setStatus(ReadPastEnd); + return skipResult; +} + +QT_END_NAMESPACE + +#endif // QT_NO_DATASTREAM diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h new file mode 100644 index 0000000000..1f1b13686c --- /dev/null +++ b/src/corelib/serialization/qdatastream.h @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDATASTREAM_H +#define QDATASTREAM_H + +#include <QtCore/qscopedpointer.h> +#include <QtCore/qiodevice.h> +#include <QtCore/qpair.h> + +#ifdef Status +#error qdatastream.h must be included before any header file that defines Status +#endif + +QT_BEGIN_NAMESPACE + +class qfloat16; +class QByteArray; +class QIODevice; + +template <typename T> class QList; +template <typename T> class QLinkedList; +template <typename T> class QVector; +template <typename T> class QSet; +template <class Key, class T> class QHash; +template <class Key, class T> class QMap; + +#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) +class QDataStreamPrivate; +namespace QtPrivate { +class StreamStateSaver; +} +class Q_CORE_EXPORT QDataStream +{ +public: + enum Version { + Qt_1_0 = 1, + Qt_2_0 = 2, + Qt_2_1 = 3, + Qt_3_0 = 4, + Qt_3_1 = 5, + Qt_3_3 = 6, + Qt_4_0 = 7, + Qt_4_1 = Qt_4_0, + Qt_4_2 = 8, + Qt_4_3 = 9, + Qt_4_4 = 10, + Qt_4_5 = 11, + Qt_4_6 = 12, + Qt_4_7 = Qt_4_6, + Qt_4_8 = Qt_4_7, + Qt_4_9 = Qt_4_8, + Qt_5_0 = 13, + Qt_5_1 = 14, + Qt_5_2 = 15, + Qt_5_3 = Qt_5_2, + Qt_5_4 = 16, + Qt_5_5 = Qt_5_4, + Qt_5_6 = 17, + Qt_5_7 = Qt_5_6, + Qt_5_8 = Qt_5_7, + Qt_5_9 = Qt_5_8, + Qt_5_10 = Qt_5_9, + Qt_5_11 = Qt_5_10, +#if QT_VERSION >= 0x050c00 +#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion +#endif + Qt_DefaultCompiledVersion = Qt_5_11 + }; + + enum ByteOrder { + BigEndian = QSysInfo::BigEndian, + LittleEndian = QSysInfo::LittleEndian + }; + + enum Status { + Ok, + ReadPastEnd, + ReadCorruptData, + WriteFailed + }; + + enum FloatingPointPrecision { + SinglePrecision, + DoublePrecision + }; + + QDataStream(); + explicit QDataStream(QIODevice *); + QDataStream(QByteArray *, QIODevice::OpenMode flags); + QDataStream(const QByteArray &); + ~QDataStream(); + + QIODevice *device() const; + void setDevice(QIODevice *); + void unsetDevice(); + + bool atEnd() const; + + Status status() const; + void setStatus(Status status); + void resetStatus(); + + FloatingPointPrecision floatingPointPrecision() const; + void setFloatingPointPrecision(FloatingPointPrecision precision); + + ByteOrder byteOrder() const; + void setByteOrder(ByteOrder); + + int version() const; + void setVersion(int); + + QDataStream &operator>>(qint8 &i); + QDataStream &operator>>(quint8 &i); + QDataStream &operator>>(qint16 &i); + QDataStream &operator>>(quint16 &i); + QDataStream &operator>>(qint32 &i); + QDataStream &operator>>(quint32 &i); + QDataStream &operator>>(qint64 &i); + QDataStream &operator>>(quint64 &i); + QDataStream &operator>>(std::nullptr_t &ptr) { ptr = nullptr; return *this; } + + QDataStream &operator>>(bool &i); + QDataStream &operator>>(qfloat16 &f); + QDataStream &operator>>(float &f); + QDataStream &operator>>(double &f); + QDataStream &operator>>(char *&str); + + QDataStream &operator<<(qint8 i); + QDataStream &operator<<(quint8 i); + QDataStream &operator<<(qint16 i); + QDataStream &operator<<(quint16 i); + QDataStream &operator<<(qint32 i); + QDataStream &operator<<(quint32 i); + QDataStream &operator<<(qint64 i); + QDataStream &operator<<(quint64 i); + QDataStream &operator<<(std::nullptr_t) { return *this; } + QDataStream &operator<<(bool i); + QDataStream &operator<<(qfloat16 f); + QDataStream &operator<<(float f); + QDataStream &operator<<(double f); + QDataStream &operator<<(const char *str); + + QDataStream &readBytes(char *&, uint &len); + int readRawData(char *, int len); + + QDataStream &writeBytes(const char *, uint len); + int writeRawData(const char *, int len); + + int skipRawData(int len); + + void startTransaction(); + bool commitTransaction(); + void rollbackTransaction(); + void abortTransaction(); + +private: + Q_DISABLE_COPY(QDataStream) + + QScopedPointer<QDataStreamPrivate> d; + + QIODevice *dev; + bool owndev; + bool noswap; + ByteOrder byteorder; + int ver; + Status q_status; + + int readBlock(char *data, int len); + friend class QtPrivate::StreamStateSaver; +}; + +namespace QtPrivate { + +class StreamStateSaver +{ +public: + inline StreamStateSaver(QDataStream *s) : stream(s), oldStatus(s->status()) + { + if (!stream->dev || !stream->dev->isTransactionStarted()) + stream->resetStatus(); + } + inline ~StreamStateSaver() + { + if (oldStatus != QDataStream::Ok) { + stream->resetStatus(); + stream->setStatus(oldStatus); + } + } + +private: + QDataStream *stream; + QDataStream::Status oldStatus; +}; + +template <typename Container> +QDataStream &readArrayBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + c.reserve(n); + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.append(t); + } + + return s; +} + +template <typename Container> +QDataStream &readListBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c << t; + } + + return s; +} + +template <typename Container> +QDataStream &readAssociativeContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::key_type k; + typename Container::mapped_type t; + s >> k >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.insertMulti(k, t); + } + + return s; +} + +template <typename Container> +QDataStream &writeSequentialContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + for (const typename Container::value_type &t : c) + s << t; + + return s; +} + +template <typename Container> +QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + // Deserialization should occur in the reverse order. + // Otherwise, value() will return the least recently inserted + // value instead of the most recently inserted one. + auto it = c.constEnd(); + auto begin = c.constBegin(); + while (it != begin) { + --it; + s << it.key() << it.value(); + } + + return s; +} + +} // QtPrivate namespace + +/***************************************************************************** + QDataStream inline functions + *****************************************************************************/ + +inline QIODevice *QDataStream::device() const +{ return dev; } + +inline QDataStream::ByteOrder QDataStream::byteOrder() const +{ return byteorder; } + +inline int QDataStream::version() const +{ return ver; } + +inline void QDataStream::setVersion(int v) +{ ver = v; } + +inline QDataStream &QDataStream::operator>>(quint8 &i) +{ return *this >> reinterpret_cast<qint8&>(i); } + +inline QDataStream &QDataStream::operator>>(quint16 &i) +{ return *this >> reinterpret_cast<qint16&>(i); } + +inline QDataStream &QDataStream::operator>>(quint32 &i) +{ return *this >> reinterpret_cast<qint32&>(i); } + +inline QDataStream &QDataStream::operator>>(quint64 &i) +{ return *this >> reinterpret_cast<qint64&>(i); } + +inline QDataStream &QDataStream::operator<<(quint8 i) +{ return *this << qint8(i); } + +inline QDataStream &QDataStream::operator<<(quint16 i) +{ return *this << qint16(i); } + +inline QDataStream &QDataStream::operator<<(quint32 i) +{ return *this << qint32(i); } + +inline QDataStream &QDataStream::operator<<(quint64 i) +{ return *this << qint64(i); } + +template <typename Enum> +inline QDataStream &operator<<(QDataStream &s, QFlags<Enum> e) +{ return s << e.i; } + +template <typename Enum> +inline QDataStream &operator>>(QDataStream &s, QFlags<Enum> &e) +{ return s >> e.i; } + +template <typename T> +inline QDataStream &operator>>(QDataStream &s, QList<T> &l) +{ + return QtPrivate::readArrayBasedContainer(s, l); +} + +template <typename T> +inline QDataStream &operator<<(QDataStream &s, const QList<T> &l) +{ + return QtPrivate::writeSequentialContainer(s, l); +} + +template <typename T> +inline QDataStream &operator>>(QDataStream &s, QLinkedList<T> &l) +{ + return QtPrivate::readListBasedContainer(s, l); +} + +template <typename T> +inline QDataStream &operator<<(QDataStream &s, const QLinkedList<T> &l) +{ + return QtPrivate::writeSequentialContainer(s, l); +} + +template<typename T> +inline QDataStream &operator>>(QDataStream &s, QVector<T> &v) +{ + return QtPrivate::readArrayBasedContainer(s, v); +} + +template<typename T> +inline QDataStream &operator<<(QDataStream &s, const QVector<T> &v) +{ + return QtPrivate::writeSequentialContainer(s, v); +} + +template <typename T> +inline QDataStream &operator>>(QDataStream &s, QSet<T> &set) +{ + return QtPrivate::readListBasedContainer(s, set); +} + +template <typename T> +inline QDataStream &operator<<(QDataStream &s, const QSet<T> &set) +{ + return QtPrivate::writeSequentialContainer(s, set); +} + +template <class Key, class T> +inline QDataStream &operator>>(QDataStream &s, QHash<Key, T> &hash) +{ + return QtPrivate::readAssociativeContainer(s, hash); +} + +template <class Key, class T> +inline QDataStream &operator<<(QDataStream &s, const QHash<Key, T> &hash) +{ + return QtPrivate::writeAssociativeContainer(s, hash); +} + +template <class Key, class T> +inline QDataStream &operator>>(QDataStream &s, QMap<Key, T> &map) +{ + return QtPrivate::readAssociativeContainer(s, map); +} + +template <class Key, class T> +inline QDataStream &operator<<(QDataStream &s, const QMap<Key, T> &map) +{ + return QtPrivate::writeAssociativeContainer(s, map); +} + +#ifndef QT_NO_DATASTREAM +template <class T1, class T2> +inline QDataStream& operator>>(QDataStream& s, QPair<T1, T2>& p) +{ + s >> p.first >> p.second; + return s; +} + +template <class T1, class T2> +inline QDataStream& operator<<(QDataStream& s, const QPair<T1, T2>& p) +{ + s << p.first << p.second; + return s; +} +#endif + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE + +#endif // QDATASTREAM_H diff --git a/src/corelib/serialization/qdatastream_p.h b/src/corelib/serialization/qdatastream_p.h new file mode 100644 index 0000000000..3ca0ae840e --- /dev/null +++ b/src/corelib/serialization/qdatastream_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDATASTREAM_P_H +#define QDATASTREAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <qdatastream.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) +class QDataStreamPrivate +{ +public: + QDataStreamPrivate() : floatingPointPrecision(QDataStream::DoublePrecision), + transactionDepth(0) { } + + QDataStream::FloatingPointPrecision floatingPointPrecision; + int transactionDepth; +}; +#endif + +QT_END_NAMESPACE + +#endif // QDATASTREAM_P_H diff --git a/src/corelib/serialization/qjson.cpp b/src/corelib/serialization/qjson.cpp new file mode 100644 index 0000000000..e4bca3bcd0 --- /dev/null +++ b/src/corelib/serialization/qjson.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjson_p.h" +#include <qalgorithms.h> + +QT_BEGIN_NAMESPACE + +namespace QJsonPrivate +{ + +static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; +static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; + +void Data::compact() +{ + Q_ASSERT(sizeof(Value) == sizeof(offset)); + + if (!compactionCounter) + return; + + Base *base = header->root(); + int reserve = 0; + if (base->is_object) { + Object *o = static_cast<Object *>(base); + for (int i = 0; i < (int)o->length; ++i) + reserve += o->entryAt(i)->usedStorage(o); + } else { + Array *a = static_cast<Array *>(base); + for (int i = 0; i < (int)a->length; ++i) + reserve += (*a)[i].usedStorage(a); + } + + int size = sizeof(Base) + reserve + base->length*sizeof(offset); + int alloc = sizeof(Header) + size; + Header *h = (Header *) malloc(alloc); + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1; + Base *b = h->root(); + b->size = size; + b->is_object = header->root()->is_object; + b->length = base->length; + b->tableOffset = reserve + sizeof(Array); + + int offset = sizeof(Base); + if (b->is_object) { + Object *o = static_cast<Object *>(base); + Object *no = static_cast<Object *>(b); + + for (int i = 0; i < (int)o->length; ++i) { + no->table()[i] = offset; + + const Entry *e = o->entryAt(i); + Entry *ne = no->entryAt(i); + int s = e->size(); + memcpy(ne, e, s); + offset += s; + int dataSize = e->value.usedStorage(o); + if (dataSize) { + memcpy((char *)no + offset, e->value.data(o), dataSize); + ne->value.value = offset; + offset += dataSize; + } + } + } else { + Array *a = static_cast<Array *>(base); + Array *na = static_cast<Array *>(b); + + for (int i = 0; i < (int)a->length; ++i) { + const Value &v = (*a)[i]; + Value &nv = (*na)[i]; + nv = v; + int dataSize = v.usedStorage(a); + if (dataSize) { + memcpy((char *)na + offset, v.data(a), dataSize); + nv.value = offset; + offset += dataSize; + } + } + } + Q_ASSERT(offset == (int)b->tableOffset); + + free(header); + header = h; + this->alloc = alloc; + compactionCounter = 0; +} + +bool Data::valid() const +{ + if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1u) + return false; + + bool res = false; + Base *root = header->root(); + int maxSize = alloc - sizeof(Header); + if (root->is_object) + res = static_cast<Object *>(root)->isValid(maxSize); + else + res = static_cast<Array *>(root)->isValid(maxSize); + + return res; +} + + +int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace) +{ + Q_ASSERT(posInTable >= 0 && posInTable <= (int)length); + if (size + dataSize >= Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure %d %d %d", (uint)size, dataSize, Value::MaxSize); + return 0; + } + + offset off = tableOffset; + // move table to new position + if (replace) { + memmove((char *)(table()) + dataSize, table(), length*sizeof(offset)); + } else { + memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset)); + memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset)); + } + tableOffset += dataSize; + for (int i = 0; i < (int)numItems; ++i) + table()[posInTable + i] = off; + size += dataSize; + if (!replace) { + length += numItems; + size += numItems * sizeof(offset); + } + return off; +} + +void Base::removeItems(int pos, int numItems) +{ + Q_ASSERT(pos >= 0 && pos <= (int)length); + if (pos + numItems < (int)length) + memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset)); + length -= numItems; +} + +int Object::indexOf(const QString &key, bool *exists) const +{ + int min = 0; + int n = length; + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= key) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < (int)length && *entryAt(min) == key) { + *exists = true; + return min; + } + *exists = false; + return min; +} + +int Object::indexOf(QLatin1String key, bool *exists) const +{ + int min = 0; + int n = length; + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= key) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < (int)length && *entryAt(min) == key) { + *exists = true; + return min; + } + *exists = false; + return min; +} + +bool Object::isValid(int maxSize) const +{ + if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) + return false; + + QString lastKey; + for (uint i = 0; i < length; ++i) { + offset entryOffset = table()[i]; + if (entryOffset + sizeof(Entry) >= tableOffset) + return false; + Entry *e = entryAt(i); + if (!e->isValid(tableOffset - table()[i])) + return false; + QString key = e->key(); + if (key < lastKey) + return false; + if (!e->value.isValid(this)) + return false; + lastKey = key; + } + return true; +} + + + +bool Array::isValid(int maxSize) const +{ + if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) + return false; + + for (uint i = 0; i < length; ++i) { + if (!at(i).isValid(this)) + return false; + } + return true; +} + + +bool Entry::operator ==(const QString &key) const +{ + if (value.latinKey) + return (shallowLatin1Key() == key); + else + return (shallowKey() == key); +} + +bool Entry::operator==(QLatin1String key) const +{ + if (value.latinKey) + return shallowLatin1Key() == key; + else + return shallowKey() == key; +} + +bool Entry::operator ==(const Entry &other) const +{ + if (value.latinKey) { + if (other.value.latinKey) + return shallowLatin1Key() == other.shallowLatin1Key(); + return shallowLatin1Key() == other.shallowKey(); + } else if (other.value.latinKey) { + return shallowKey() == other.shallowLatin1Key(); + } + return shallowKey() == other.shallowKey(); +} + +bool Entry::operator >=(const Entry &other) const +{ + if (value.latinKey) { + if (other.value.latinKey) + return shallowLatin1Key() >= other.shallowLatin1Key(); + return shallowLatin1Key() >= other.shallowKey(); + } else if (other.value.latinKey) { + return shallowKey() >= other.shallowLatin1Key(); + } + return shallowKey() >= other.shallowKey(); +} + + +int Value::usedStorage(const Base *b) const +{ + int s = 0; + switch (type) { + case QJsonValue::Double: + if (latinOrIntValue) + break; + s = sizeof(double); + break; + case QJsonValue::String: { + char *d = data(b); + if (latinOrIntValue) + s = sizeof(ushort) + qFromLittleEndian(*(ushort *)d); + else + s = sizeof(int) + sizeof(ushort) * qFromLittleEndian(*(int *)d); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: + s = base(b)->size; + break; + case QJsonValue::Null: + case QJsonValue::Bool: + default: + break; + } + return alignedSize(s); +} + +bool Value::isValid(const Base *b) const +{ + int offset = 0; + switch (type) { + case QJsonValue::Double: + if (latinOrIntValue) + break; + Q_FALLTHROUGH(); + case QJsonValue::String: + case QJsonValue::Array: + case QJsonValue::Object: + offset = value; + break; + case QJsonValue::Null: + case QJsonValue::Bool: + default: + break; + } + + if (!offset) + return true; + if (offset + sizeof(uint) > b->tableOffset) + return false; + + int s = usedStorage(b); + if (!s) + return true; + if (s < 0 || s > (int)b->tableOffset - offset) + return false; + if (type == QJsonValue::Array) + return static_cast<Array *>(base(b))->isValid(s); + if (type == QJsonValue::Object) + return static_cast<Object *>(base(b))->isValid(s); + return true; +} + +/*! + \internal + */ +int Value::requiredStorage(QJsonValue &v, bool *compressed) +{ + *compressed = false; + switch (v.t) { + case QJsonValue::Double: + if (QJsonPrivate::compressedNumber(v.dbl) != INT_MAX) { + *compressed = true; + return 0; + } + return sizeof(double); + case QJsonValue::String: { + QString s = v.toString(); + *compressed = QJsonPrivate::useCompressed(s); + return QJsonPrivate::qStringSize(s, *compressed); + } + case QJsonValue::Array: + case QJsonValue::Object: + if (v.d && v.d->compactionCounter) { + v.detach(); + v.d->compact(); + v.base = static_cast<QJsonPrivate::Base *>(v.d->header->root()); + } + return v.base ? uint(v.base->size) : sizeof(QJsonPrivate::Base); + case QJsonValue::Undefined: + case QJsonValue::Null: + case QJsonValue::Bool: + break; + } + return 0; +} + +/*! + \internal + */ +uint Value::valueToStore(const QJsonValue &v, uint offset) +{ + switch (v.t) { + case QJsonValue::Undefined: + case QJsonValue::Null: + break; + case QJsonValue::Bool: + return v.b; + case QJsonValue::Double: { + int c = QJsonPrivate::compressedNumber(v.dbl); + if (c != INT_MAX) + return c; + } + Q_FALLTHROUGH(); + case QJsonValue::String: + case QJsonValue::Array: + case QJsonValue::Object: + return offset; + } + return 0; +} + +/*! + \internal + */ +void Value::copyData(const QJsonValue &v, char *dest, bool compressed) +{ + switch (v.t) { + case QJsonValue::Double: + if (!compressed) { + qToLittleEndian(v.ui, dest); + } + break; + case QJsonValue::String: { + QString str = v.toString(); + QJsonPrivate::copyString(dest, str, compressed); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: { + const QJsonPrivate::Base *b = v.base; + if (!b) + b = (v.t == QJsonValue::Array ? &emptyArray : &emptyObject); + memcpy(dest, b, b->size); + break; + } + default: + break; + } +} + +} // namespace QJsonPrivate + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h new file mode 100644 index 0000000000..7743382806 --- /dev/null +++ b/src/corelib/serialization/qjson_p.h @@ -0,0 +1,774 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSON_P_H +#define QJSON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsondocument.h> +#include <qjsonarray.h> +#include <qatomic.h> +#include <qstring.h> +#include <qendian.h> +#include <qnumeric.h> + +#include "private/qendian_p.h" +#include "private/qsimd_p.h" + +#include <limits.h> +#include <limits> + +QT_BEGIN_NAMESPACE + +/* + This defines a binary data structure for Json data. The data structure is optimised for fast reading + and minimum allocations. The whole data structure can be mmap'ed and used directly. + + In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but + much faster to access. + + The size requirements are: + + String: + Latin1 data: 2 bytes header + string.length() + Full Unicode: 4 bytes header + 2*(string.length()) + + Values: 4 bytes + size of data (size can be 0 for some data) + bool: 0 bytes + double: 8 bytes (0 if integer with less than 27bits) + string: see above + array: size of array + object: size of object + Array: 12 bytes + 4*length + size of Value data + Object: 12 bytes + 8*length + size of Key Strings + size of Value data + + For an example such as + + { // object: 12 + 5*8 = 52 + "firstName": "John", // key 12, value 8 = 20 + "lastName" : "Smith", // key 12, value 8 = 20 + "age" : 25, // key 8, value 0 = 8 + "address" : // key 12, object below = 140 + { // object: 12 + 4*8 + "streetAddress": "21 2nd Street", // key 16, value 16 + "city" : "New York", // key 8, value 12 + "state" : "NY", // key 8, value 4 + "postalCode" : "10021" // key 12, value 8 + }, // object total: 128 + "phoneNumber": // key: 16, value array below = 172 + [ // array: 12 + 2*4 + values below: 156 + { // object 12 + 2*8 + "type" : "home", // key 8, value 8 + "number": "212 555-1234" // key 8, value 16 + }, // object total: 68 + { // object 12 + 2*8 + "type" : "fax", // key 8, value 8 + "number": "646 555-4567" // key 8, value 16 + } // object total: 68 + ] // array total: 156 + } // great total: 412 bytes + + The uncompressed text file used roughly 500 bytes, so in this case we end up using about + the same space as the text representation. + + Other measurements have shown a slightly bigger binary size than a compact text + representation where all possible whitespace was stripped out. +*/ +#define Q_DECLARE_JSONPRIVATE_TYPEINFO(Class, Flags) } Q_DECLARE_TYPEINFO(QJsonPrivate::Class, Flags); namespace QJsonPrivate { +namespace QJsonPrivate { + +class Array; +class Object; +class Value; +class Entry; + +template<typename T> +using q_littleendian = QLEInteger<T>; + +typedef q_littleendian<short> qle_short; +typedef q_littleendian<unsigned short> qle_ushort; +typedef q_littleendian<int> qle_int; +typedef q_littleendian<unsigned int> qle_uint; + +template<int pos, int width> +using qle_bitfield = QLEIntegerBitfield<uint, pos, width>; + +template<int pos, int width> +using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>; + +typedef qle_uint offset; + +// round the size up to the next 4 byte boundary +inline int alignedSize(int size) { return (size + 3) & ~3; } + +static inline bool useCompressed(const QString &s) +{ + if (s.length() >= 0x8000) + return false; + return QtPrivate::isLatin1(s); +} + +static inline int qStringSize(const QString &string, bool compress) +{ + int l = 2 + string.length(); + if (!compress) + l *= 2; + return alignedSize(l); +} + +// returns INT_MAX if it can't compress it into 28 bits +static inline int compressedNumber(double d) +{ + // this relies on details of how ieee floats are represented + const int exponent_off = 52; + const quint64 fraction_mask = 0x000fffffffffffffull; + const quint64 exponent_mask = 0x7ff0000000000000ull; + + quint64 val; + memcpy (&val, &d, sizeof(double)); + int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; + if (exp < 0 || exp > 25) + return INT_MAX; + + quint64 non_int = val & (fraction_mask >> exp); + if (non_int) + return INT_MAX; + + bool neg = (val >> 63) != 0; + val &= fraction_mask; + val |= ((quint64)1 << 52); + int res = (int)(val >> (52 - exp)); + return neg ? -res : res; +} + +class Latin1String; + +class String +{ +public: + explicit String(const char *data) { d = (Data *)data; } + + struct Data { + qle_uint length; + qle_ushort utf16[1]; + }; + + Data *d; + + int byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; } + bool isValid(int maxSize) const { + // Check byteSize() <= maxSize, avoiding integer overflow + maxSize -= sizeof(uint); + return maxSize >= 0 && uint(d->length) <= maxSize / sizeof(ushort); + } + + inline String &operator=(const QString &str) + { + d->length = str.length(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + const ushort *uc = (const ushort *)str.unicode(); + for (int i = 0; i < str.length(); ++i) + d->utf16[i] = uc[i]; +#else + memcpy(d->utf16, str.unicode(), str.length()*sizeof(ushort)); +#endif + if (str.length() & 1) + d->utf16[str.length()] = 0; + return *this; + } + + inline bool operator ==(const QString &str) const { + int slen = str.length(); + int l = d->length; + if (slen != l) + return false; + const ushort *s = (const ushort *)str.constData(); + const qle_ushort *a = d->utf16; + const ushort *b = s; + while (l-- && *a == *b) + a++,b++; + return (l == -1); + } + inline bool operator !=(const QString &str) const { + return !operator ==(str); + } + inline bool operator >=(const QString &str) const { + // ### + return toString() >= str; + } + + inline bool operator<(const Latin1String &str) const; + inline bool operator>=(const Latin1String &str) const { return !operator <(str); } + inline bool operator ==(const Latin1String &str) const; + + inline bool operator ==(const String &str) const { + if (d->length != str.d->length) + return false; + return !memcmp(d->utf16, str.d->utf16, d->length*sizeof(ushort)); + } + inline bool operator<(const String &other) const; + inline bool operator >=(const String &other) const { return !(*this < other); } + + inline QString toString() const { +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + return QString((QChar *)d->utf16, d->length); +#else + int l = d->length; + QString str(l, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < l; ++i) + ch[i] = QChar(d->utf16[i]); + return str; +#endif + } + +}; + +class Latin1String +{ +public: + explicit Latin1String(const char *data) { d = (Data *)data; } + + struct Data { + qle_ushort length; + char latin1[1]; + }; + Data *d; + + int byteSize() const { return sizeof(ushort) + sizeof(char)*(d->length); } + bool isValid(int maxSize) const { + return byteSize() <= maxSize; + } + + inline Latin1String &operator=(const QString &str) + { + int len = d->length = str.length(); + uchar *l = (uchar *)d->latin1; + const ushort *uc = (const ushort *)str.unicode(); + int i = 0; +#ifdef __SSE2__ + for ( ; i + 16 <= len; i += 16) { + __m128i chunk1 = _mm_loadu_si128((__m128i*)&uc[i]); // load + __m128i chunk2 = _mm_loadu_si128((__m128i*)&uc[i + 8]); // load + // pack the two vector to 16 x 8bits elements + const __m128i result = _mm_packus_epi16(chunk1, chunk2); + _mm_storeu_si128((__m128i*)&l[i], result); // store + } +# ifdef Q_PROCESSOR_X86_64 + // we can do one more round, of 8 characters + if (i + 8 <= len) { + __m128i chunk = _mm_loadu_si128((__m128i*)&uc[i]); // load + // pack with itself, we'll discard the high part anyway + chunk = _mm_packus_epi16(chunk, chunk); + // unaligned 64-bit store + qToUnaligned(_mm_cvtsi128_si64(chunk), l + i); + i += 8; + } +# endif +#endif + for ( ; i < len; ++i) + l[i] = uc[i]; + for ( ; (quintptr)(l+i) & 0x3; ++i) + l[i] = 0; + return *this; + } + + QLatin1String toQLatin1String() const Q_DECL_NOTHROW { + return QLatin1String(d->latin1, d->length); + } + + inline bool operator<(const String &str) const + { + const qle_ushort *uc = (qle_ushort *) str.d->utf16; + if (!uc || *uc == 0) + return false; + + const uchar *c = (uchar *)d->latin1; + const uchar *e = c + qMin((int)d->length, (int)str.d->length); + + while (c < e) { + if (*c != *uc) + break; + ++c; + ++uc; + } + return (c == e ? (int)d->length < (int)str.d->length : *c < *uc); + + } + inline bool operator ==(const String &str) const { + return (str == *this); + } + inline bool operator >=(const String &str) const { + return !(*this < str); + } + + inline QString toString() const { + return QString::fromLatin1(d->latin1, d->length); + } +}; + +#define DEF_OP(op) \ + inline bool operator op(Latin1String lhs, Latin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs.toQLatin1String() op rhs.toQLatin1String(); \ + } \ + inline bool operator op(QLatin1String lhs, Latin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs op rhs.toQLatin1String(); \ + } \ + inline bool operator op(Latin1String lhs, QLatin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs.toQLatin1String() op rhs; \ + } \ + inline bool operator op(const QString &lhs, Latin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs op rhs.toQLatin1String(); \ + } \ + inline bool operator op(Latin1String lhs, const QString &rhs) Q_DECL_NOTHROW \ + { \ + return lhs.toQLatin1String() op rhs; \ + } \ + /*end*/ +DEF_OP(==) +DEF_OP(!=) +DEF_OP(< ) +DEF_OP(> ) +DEF_OP(<=) +DEF_OP(>=) +#undef DEF_OP + +inline bool String::operator ==(const Latin1String &str) const +{ + if ((int)d->length != (int)str.d->length) + return false; + const qle_ushort *uc = d->utf16; + const qle_ushort *e = uc + d->length; + const uchar *c = (uchar *)str.d->latin1; + + while (uc < e) { + if (*uc != *c) + return false; + ++uc; + ++c; + } + return true; +} + +inline bool String::operator <(const String &other) const +{ + int alen = d->length; + int blen = other.d->length; + int l = qMin(alen, blen); + qle_ushort *a = d->utf16; + qle_ushort *b = other.d->utf16; + + while (l-- && *a == *b) + a++,b++; + if (l==-1) + return (alen < blen); + return (ushort)*a < (ushort)*b; +} + +inline bool String::operator<(const Latin1String &str) const +{ + const uchar *c = (uchar *) str.d->latin1; + if (!c || *c == 0) + return false; + + const qle_ushort *uc = d->utf16; + const qle_ushort *e = uc + qMin((int)d->length, (int)str.d->length); + + while (uc < e) { + if (*uc != *c) + break; + ++uc; + ++c; + } + return (uc == e ? (int)d->length < (int)str.d->length : (ushort)*uc < *c); + +} + +static inline void copyString(char *dest, const QString &str, bool compress) +{ + if (compress) { + Latin1String string(dest); + string = str; + } else { + String string(dest); + string = str; + } +} + + +/* + Base is the base class for both Object and Array. Both classe work more or less the same way. + The class starts with a header (defined by the struct below), then followed by data (the data for + values in the Array case and Entry's (see below) for objects. + + After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and + offsets from the beginning of the object to Entry's in the case of Object. + + Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage + of a binary search over the keys in an Object. + */ +class Base +{ +public: + qle_uint size; + union { + uint _dummy; + qle_bitfield<0, 1> is_object; + qle_bitfield<1, 31> length; + }; + offset tableOffset; + // content follows here + + inline bool isObject() const { return !!is_object; } + inline bool isArray() const { return !isObject(); } + + inline offset *table() const { return (offset *) (((char *) this) + tableOffset); } + + int reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace); + void removeItems(int pos, int numItems); +}; + +class Object : public Base +{ +public: + Entry *entryAt(int i) const { + return reinterpret_cast<Entry *>(((char *)this) + table()[i]); + } + int indexOf(const QString &key, bool *exists) const; + int indexOf(QLatin1String key, bool *exists) const; + + bool isValid(int maxSize) const; +}; + + +class Array : public Base +{ +public: + inline Value at(int i) const; + inline Value &operator [](int i); + + bool isValid(int maxSize) const; +}; + + +class Value +{ +public: + enum { + MaxSize = (1<<27) - 1 + }; + union { + uint _dummy; + qle_bitfield<0, 3> type; + qle_bitfield<3, 1> latinOrIntValue; + qle_bitfield<4, 1> latinKey; + qle_bitfield<5, 27> value; + qle_signedbitfield<5, 27> int_value; + }; + + inline char *data(const Base *b) const { return ((char *)b) + value; } + int usedStorage(const Base *b) const; + + bool toBoolean() const; + double toDouble(const Base *b) const; + QString toString(const Base *b) const; + String asString(const Base *b) const; + Latin1String asLatin1String(const Base *b) const; + Base *base(const Base *b) const; + + bool isValid(const Base *b) const; + + static int requiredStorage(QJsonValue &v, bool *compressed); + static uint valueToStore(const QJsonValue &v, uint offset); + static void copyData(const QJsonValue &v, char *dest, bool compressed); +}; +Q_DECLARE_JSONPRIVATE_TYPEINFO(Value, Q_PRIMITIVE_TYPE) + +inline Value Array::at(int i) const +{ + return *(Value *) (table() + i); +} + +inline Value &Array::operator [](int i) +{ + return *(Value *) (table() + i); +} + + + +class Entry { +public: + Value value; + // key + // value data follows key + + uint size() const { + int s = sizeof(Entry); + if (value.latinKey) + s += shallowLatin1Key().byteSize(); + else + s += shallowKey().byteSize(); + return alignedSize(s); + } + + int usedStorage(Base *b) const { + return size() + value.usedStorage(b); + } + + String shallowKey() const + { + Q_ASSERT(!value.latinKey); + return String((const char *)this + sizeof(Entry)); + } + Latin1String shallowLatin1Key() const + { + Q_ASSERT(value.latinKey); + return Latin1String((const char *)this + sizeof(Entry)); + } + QString key() const + { + if (value.latinKey) { + return shallowLatin1Key().toString(); + } + return shallowKey().toString(); + } + + bool isValid(int maxSize) const { + if (maxSize < (int)sizeof(Entry)) + return false; + maxSize -= sizeof(Entry); + if (value.latinKey) + return shallowLatin1Key().isValid(maxSize); + return shallowKey().isValid(maxSize); + } + + bool operator ==(const QString &key) const; + inline bool operator !=(const QString &key) const { return !operator ==(key); } + inline bool operator >=(const QString &key) const; + + bool operator==(QLatin1String key) const; + inline bool operator!=(QLatin1String key) const { return !operator ==(key); } + inline bool operator>=(QLatin1String key) const; + + bool operator ==(const Entry &other) const; + bool operator >=(const Entry &other) const; +}; + +inline bool Entry::operator >=(const QString &key) const +{ + if (value.latinKey) + return (shallowLatin1Key() >= key); + else + return (shallowKey() >= key); +} + +inline bool Entry::operator >=(QLatin1String key) const +{ + if (value.latinKey) + return shallowLatin1Key() >= key; + else + return shallowKey() >= key; +} + +inline bool operator <(const QString &key, const Entry &e) +{ return e >= key; } + +inline bool operator<(QLatin1String key, const Entry &e) +{ return e >= key; } + + +class Header { +public: + qle_uint tag; // 'qbjs' + qle_uint version; // 1 + Base *root() { return (Base *)(this + 1); } +}; + + +inline bool Value::toBoolean() const +{ + Q_ASSERT(type == QJsonValue::Bool); + return value != 0; +} + +inline double Value::toDouble(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::Double); + if (latinOrIntValue) + return int_value; + + quint64 i = qFromLittleEndian<quint64>((const uchar *)b + value); + double d; + memcpy(&d, &i, sizeof(double)); + return d; +} + +inline String Value::asString(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::String && !latinOrIntValue); + return String(data(b)); +} + +inline Latin1String Value::asLatin1String(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::String && latinOrIntValue); + return Latin1String(data(b)); +} + +inline QString Value::toString(const Base *b) const +{ + if (latinOrIntValue) + return asLatin1String(b).toString(); + else + return asString(b).toString(); +} + +inline Base *Value::base(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object); + return reinterpret_cast<Base *>(data(b)); +} + +class Data { +public: + enum Validation { + Unchecked, + Validated, + Invalid + }; + + QAtomicInt ref; + int alloc; + union { + char *rawData; + Header *header; + }; + uint compactionCounter : 31; + uint ownsData : 1; + + inline Data(char *raw, int a) + : alloc(a), rawData(raw), compactionCounter(0), ownsData(true) + { + } + inline Data(int reserved, QJsonValue::Type valueType) + : rawData(0), compactionCounter(0), ownsData(true) + { + Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object); + + alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset); + header = (Header *)malloc(alloc); + Q_CHECK_PTR(header); + header->tag = QJsonDocument::BinaryFormatTag; + header->version = 1; + Base *b = header->root(); + b->size = sizeof(Base); + b->is_object = (valueType == QJsonValue::Object); + b->tableOffset = sizeof(Base); + b->length = 0; + } + inline ~Data() + { if (ownsData) free(rawData); } + + uint offsetOf(const void *ptr) const { return (uint)(((char *)ptr - rawData)); } + + QJsonObject toObject(Object *o) const + { + return QJsonObject(const_cast<Data *>(this), o); + } + + QJsonArray toArray(Array *a) const + { + return QJsonArray(const_cast<Data *>(this), a); + } + + Data *clone(Base *b, int reserve = 0) + { + int size = sizeof(Header) + b->size; + if (b == header->root() && ref.load() == 1 && alloc >= size + reserve) + return this; + + if (reserve) { + if (reserve < 128) + reserve = 128; + size = qMax(size + reserve, qMin(size *2, (int)Value::MaxSize)); + if (size > Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return 0; + } + } + char *raw = (char *)malloc(size); + Q_CHECK_PTR(raw); + memcpy(raw + sizeof(Header), b, b->size); + Header *h = (Header *)raw; + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1; + Data *d = new Data(raw, size); + d->compactionCounter = (b == header->root()) ? compactionCounter : 0; + return d; + } + + void compact(); + bool valid() const; + +private: + Q_DISABLE_COPY(Data) +}; + +} + +QT_END_NAMESPACE + +#endif // QJSON_P_H diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp new file mode 100644 index 0000000000..c5a5aaf39d --- /dev/null +++ b/src/corelib/serialization/qjsonarray.cpp @@ -0,0 +1,1258 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsonarray.h> +#include <qstringlist.h> +#include <qvariant.h> +#include <qdebug.h> + +#include "qjsonwriter_p.h" +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJsonArray + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonArray class encapsulates a JSON array. + + A JSON array is a list of values. The list can be manipulated by inserting and + removing QJsonValue's from the array. + + A QJsonArray can be converted to and from a QVariantList. You can query the + number of entries with size(), insert(), and removeAt() entries from it + and iterate over its content using the standard C++ iterator pattern. + + QJsonArray is an implicitly shared class and shares the data with the document + it has been created from as long as it is not being modified. + + You can convert the array to and from text based JSON through QJsonDocument. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \typedef QJsonArray::Iterator + + Qt-style synonym for QJsonArray::iterator. +*/ + +/*! + \typedef QJsonArray::ConstIterator + + Qt-style synonym for QJsonArray::const_iterator. +*/ + +/*! + \typedef QJsonArray::size_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::value_type + + Typedef for QJsonValue. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::difference_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::pointer + + Typedef for QJsonValue *. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::const_pointer + + Typedef for const QJsonValue *. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::reference + + Typedef for QJsonValue &. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::const_reference + + Typedef for const QJsonValue &. Provided for STL compatibility. +*/ + +/*! + Creates an empty array. + */ +QJsonArray::QJsonArray() + : d(0), a(0) +{ +} + +/*! + \fn QJsonArray::QJsonArray(std::initializer_list<QJsonValue> args) + \since 5.4 + Creates an array initialized from \a args initialization list. + + QJsonArray can be constructed in a way similar to JSON notation, + for example: + \code + QJsonArray array = { 1, 2.2, QString() }; + \endcode + */ + +/*! + \internal + */ +QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) + : d(data), a(array) +{ + Q_ASSERT(data); + Q_ASSERT(array); + d->ref.ref(); +} + +/*! + This method replaces part of QJsonArray(std::initializer_list<QJsonValue> args) . + The constructor needs to be inline, but we do not want to leak implementation details + of this class. + \note this method is called for an uninitialized object + \internal + */ +void QJsonArray::initialize() +{ + d = 0; + a = 0; +} + +/*! + Deletes the array. + */ +QJsonArray::~QJsonArray() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + + Since QJsonArray is implicitly shared, the copy is shallow + as long as the object doesn't get modified. + */ +QJsonArray::QJsonArray(const QJsonArray &other) +{ + d = other.d; + a = other.a; + if (d) + d->ref.ref(); +} + +/*! + Assigns \a other to this array. + */ +QJsonArray &QJsonArray::operator =(const QJsonArray &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + a = other.a; + + return *this; +} + +/*! + \fn QJsonArray::QJsonArray(QJsonArray &&other) + \since 5.10 + + Move-constructs a QJsonArray from \a other. +*/ + +/*! + \fn QJsonArray &QJsonArray::operator =(QJsonArray &&other) + \since 5.10 + + Move-assigns \a other to this array. +*/ + +/*! + \fn void QJsonArray::swap(QJsonArray &other) + \since 5.10 + + Swaps the array \a other with this. This operation is very fast and never fails. +*/ + +/*! \fn QJsonArray &QJsonArray::operator+=(const QJsonValue &value) + + Appends \a value to the array, and returns a reference to the array itself. + + \since 5.3 + \sa append(), operator<<() +*/ + +/*! \fn QJsonArray QJsonArray::operator+(const QJsonValue &value) const + + Returns an array that contains all the items in this array followed + by the provided \a value. + + \since 5.3 + \sa operator+=() +*/ + +/*! \fn QJsonArray &QJsonArray::operator<<(const QJsonValue &value) + + Appends \a value to the array, and returns a reference to the array itself. + + \since 5.3 + \sa operator+=(), append() +*/ + +/*! + Converts the string list \a list to a QJsonArray. + + The values in \a list will be converted to JSON values. + + \sa toVariantList(), QJsonValue::fromVariant() + */ +QJsonArray QJsonArray::fromStringList(const QStringList &list) +{ + QJsonArray array; + for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) + array.append(QJsonValue(*it)); + return array; +} + +/*! + Converts the variant list \a list to a QJsonArray. + + The QVariant values in \a list will be converted to JSON values. + + \sa toVariantList(), QJsonValue::fromVariant() + */ +QJsonArray QJsonArray::fromVariantList(const QVariantList &list) +{ + QJsonArray array; + if (list.isEmpty()) + return array; + + array.detach2(1024); + + QVector<QJsonPrivate::Value> values; + values.resize(list.size()); + QJsonPrivate::Value *valueData = values.data(); + uint currentOffset = sizeof(QJsonPrivate::Base); + + for (int i = 0; i < list.size(); ++i) { + QJsonValue val = QJsonValue::fromVariant(list.at(i)); + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + if (!array.detach2(valueSize)) + return QJsonArray(); + + QJsonPrivate::Value *v = valueData + i; + v->type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v->latinOrIntValue = latinOrIntValue; + v->latinKey = false; + v->value = QJsonPrivate::Value::valueToStore(val, currentOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)array.a + currentOffset, latinOrIntValue); + + currentOffset += valueSize; + array.a->size = currentOffset; + } + + // write table + array.a->tableOffset = currentOffset; + if (!array.detach2(sizeof(QJsonPrivate::offset)*values.size())) + return QJsonArray(); + memcpy(array.a->table(), values.constData(), values.size()*sizeof(uint)); + array.a->length = values.size(); + array.a->size = currentOffset + sizeof(QJsonPrivate::offset)*values.size(); + + return array; +} + +/*! + Converts this object to a QVariantList. + + Returns the created map. + */ +QVariantList QJsonArray::toVariantList() const +{ + QVariantList list; + + if (a) { + list.reserve(a->length); + for (int i = 0; i < (int)a->length; ++i) + list.append(QJsonValue(d, a, a->at(i)).toVariant()); + } + return list; +} + + +/*! + Returns the number of values stored in the array. + */ +int QJsonArray::size() const +{ + if (!d) + return 0; + + return (int)a->length; +} + +/*! + \fn QJsonArray::count() const + + Same as size(). + + \sa size() +*/ + +/*! + Returns \c true if the object is empty. This is the same as size() == 0. + + \sa size() + */ +bool QJsonArray::isEmpty() const +{ + if (!d) + return true; + + return !a->length; +} + +/*! + Returns a QJsonValue representing the value for index \a i. + + The returned QJsonValue is \c Undefined, if \a i is out of bounds. + + */ +QJsonValue QJsonArray::at(int i) const +{ + if (!a || i < 0 || i >= (int)a->length) + return QJsonValue(QJsonValue::Undefined); + + return QJsonValue(d, a, a->at(i)); +} + +/*! + Returns the first value stored in the array. + + Same as \c at(0). + + \sa at() + */ +QJsonValue QJsonArray::first() const +{ + return at(0); +} + +/*! + Returns the last value stored in the array. + + Same as \c{at(size() - 1)}. + + \sa at() + */ +QJsonValue QJsonArray::last() const +{ + return at(a ? (a->length - 1) : 0); +} + +/*! + Inserts \a value at the beginning of the array. + + This is the same as \c{insert(0, value)} and will prepend \a value to the array. + + \sa append(), insert() + */ +void QJsonArray::prepend(const QJsonValue &value) +{ + insert(0, value); +} + +/*! + Inserts \a value at the end of the array. + + \sa prepend(), insert() + */ +void QJsonArray::append(const QJsonValue &value) +{ + insert(a ? (int)a->length : 0, value); +} + +/*! + Removes the value at index position \a i. \a i must be a valid + index position in the array (i.e., \c{0 <= i < size()}). + + \sa insert(), replace() + */ +void QJsonArray::removeAt(int i) +{ + if (!a || i < 0 || i >= (int)a->length) + return; + + detach2(); + a->removeItems(i, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) + compact(); +} + +/*! \fn void QJsonArray::removeFirst() + + Removes the first item in the array. Calling this function is + equivalent to calling \c{removeAt(0)}. The array must not be empty. If + the array can be empty, call isEmpty() before calling this + function. + + \sa removeAt(), removeLast() +*/ + +/*! \fn void QJsonArray::removeLast() + + Removes the last item in the array. Calling this function is + equivalent to calling \c{removeAt(size() - 1)}. The array must not be + empty. If the array can be empty, call isEmpty() before calling + this function. + + \sa removeAt(), removeFirst() +*/ + +/*! + Removes the item at index position \a i and returns it. \a i must + be a valid index position in the array (i.e., \c{0 <= i < size()}). + + If you don't use the return value, removeAt() is more efficient. + + \sa removeAt() + */ +QJsonValue QJsonArray::takeAt(int i) +{ + if (!a || i < 0 || i >= (int)a->length) + return QJsonValue(QJsonValue::Undefined); + + QJsonValue v(d, a, a->at(i)); + removeAt(i); // detaches + return v; +} + +/*! + Inserts \a value at index position \a i in the array. If \a i + is \c 0, the value is prepended to the array. If \a i is size(), the + value is appended to the array. + + \sa append(), prepend(), replace(), removeAt() + */ +void QJsonArray::insert(int i, const QJsonValue &value) +{ + Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0)); + QJsonValue val = value; + + bool compressed; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); + + if (!detach2(valueSize + sizeof(QJsonPrivate::Value))) + return; + + if (!a->length) + a->tableOffset = sizeof(QJsonPrivate::Array); + + int valueOffset = a->reserveSpace(valueSize, i, 1, false); + if (!valueOffset) + return; + + QJsonPrivate::Value &v = (*a)[i]; + v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v.latinOrIntValue = compressed; + v.latinKey = false; + v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); +} + +/*! + \fn QJsonArray::iterator QJsonArray::insert(iterator before, const QJsonValue &value) + + Inserts \a value before the position pointed to by \a before, and returns an iterator + pointing to the newly inserted item. + + \sa erase(), insert() +*/ + +/*! + \fn QJsonArray::iterator QJsonArray::erase(iterator it) + + Removes the item pointed to by \a it, and returns an iterator pointing to the + next item. + + \sa removeAt() +*/ + +/*! + Replaces the item at index position \a i with \a value. \a i must + be a valid index position in the array (i.e., \c{0 <= i < size()}). + + \sa operator[](), removeAt() + */ +void QJsonArray::replace(int i, const QJsonValue &value) +{ + Q_ASSERT (a && i >= 0 && i < (int)(a->length)); + QJsonValue val = value; + + bool compressed; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); + + if (!detach2(valueSize)) + return; + + if (!a->length) + a->tableOffset = sizeof(QJsonPrivate::Array); + + int valueOffset = a->reserveSpace(valueSize, i, 1, true); + if (!valueOffset) + return; + + QJsonPrivate::Value &v = (*a)[i]; + v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v.latinOrIntValue = compressed; + v.latinKey = false; + v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); + + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) + compact(); +} + +/*! + Returns \c true if the array contains an occurrence of \a value, otherwise \c false. + + \sa count() + */ +bool QJsonArray::contains(const QJsonValue &value) const +{ + for (int i = 0; i < size(); i++) { + if (at(i) == value) + return true; + } + return false; +} + +/*! + Returns the value at index position \a i as a modifiable reference. + \a i must be a valid index position in the array (i.e., \c{0 <= i < + size()}). + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the character in the QJsonArray of QJsonObject + from which you got the reference. + + \sa at() + */ +QJsonValueRef QJsonArray::operator [](int i) +{ + Q_ASSERT(a && i >= 0 && i < (int)a->length); + return QJsonValueRef(this, i); +} + +/*! + \overload + + Same as at(). + */ +QJsonValue QJsonArray::operator[](int i) const +{ + return at(i); +} + +/*! + Returns \c true if this array is equal to \a other. + */ +bool QJsonArray::operator==(const QJsonArray &other) const +{ + if (a == other.a) + return true; + + if (!a) + return !other.a->length; + if (!other.a) + return !a->length; + if (a->length != other.a->length) + return false; + + for (int i = 0; i < (int)a->length; ++i) { + if (QJsonValue(d, a, a->at(i)) != QJsonValue(other.d, other.a, other.a->at(i))) + return false; + } + return true; +} + +/*! + Returns \c true if this array is not equal to \a other. + */ +bool QJsonArray::operator!=(const QJsonArray &other) const +{ + return !(*this == other); +} + +/*! \fn QJsonArray::iterator QJsonArray::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the array. + + \sa constBegin(), end() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::begin() const + + \overload +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn const_iterator QJsonArray::end() const + + \overload +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the array. + + \sa constBegin(), end() +*/ + +/*! \fn void QJsonArray::push_back(const QJsonValue &value) + + This function is provided for STL compatibility. It is equivalent + to \l{QJsonArray::append()}{append(value)} and will append \a value to the array. +*/ + +/*! \fn void QJsonArray::push_front(const QJsonValue &value) + + This function is provided for STL compatibility. It is equivalent + to \l{QJsonArray::prepend()}{prepend(value)} and will prepend \a value to the array. +*/ + +/*! \fn void QJsonArray::pop_front() + + This function is provided for STL compatibility. It is equivalent + to removeFirst(). The array must not be empty. If the array can be + empty, call isEmpty() before calling this function. +*/ + +/*! \fn void QJsonArray::pop_back() + + This function is provided for STL compatibility. It is equivalent + to removeLast(). The array must not be empty. If the array can be + empty, call isEmpty() before calling this function. +*/ + +/*! \fn bool QJsonArray::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty() and returns \c true if the array is empty. +*/ + +/*! \class QJsonArray::iterator + \inmodule QtCore + \brief The QJsonArray::iterator class provides an STL-style non-const iterator for QJsonArray. + + QJsonArray::iterator allows you to iterate over a QJsonArray + and to modify the array item associated with the + iterator. If you want to iterate over a const QJsonArray, use + QJsonArray::const_iterator instead. It is generally a good practice to + use QJsonArray::const_iterator on a non-const QJsonArray as well, unless + you need to change the QJsonArray through the iterator. Const + iterators are slightly faster and improves code readability. + + The default QJsonArray::iterator constructor creates an uninitialized + iterator. You must initialize it using a QJsonArray function like + QJsonArray::begin(), QJsonArray::end(), or QJsonArray::insert() before you can + start iterating. + + Most QJsonArray functions accept an integer index rather than an + iterator. For that reason, iterators are rarely useful in + connection with QJsonArray. One place where STL-style iterators do + make sense is as arguments to \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be + aware that any non-const function call performed on the QJsonArray + will render all existing iterators undefined. + + \sa QJsonArray::const_iterator +*/ + +/*! \typedef QJsonArray::iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random access iterator. +*/ + +/*! \typedef QJsonArray::iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonArray::iterator::value_type + + \internal +*/ + +/*! \typedef QJsonArray::iterator::reference + + \internal +*/ + +/*! \typedef QJsonArray::iterator::pointer + + \internal +*/ + +/*! \fn QJsonArray::iterator::iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called + on an uninitialized iterator. Use operator=() to assign a value + to it before using it. + + \sa QJsonArray::begin(), QJsonArray::end() +*/ + +/*! \fn QJsonArray::iterator::iterator(QJsonArray *array, int index) + \internal +*/ + +/*! \fn QJsonValueRef QJsonArray::iterator::operator*() const + + + Returns a modifiable reference to the current item. + + You can change the value of an item by using operator*() on the + left side of an assignment. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the character in the QJsonArray of QJsonObject + from which you got the reference. +*/ + +/*! \fn QJsonValueRef *QJsonArray::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! \fn QJsonValueRef QJsonArray::iterator::operator[](int j) const + + Returns a modifiable reference to the item at offset \a j from the + item pointed to by this iterator (the item at position \c{*this + j}). + + This function is provided to make QJsonArray iterators behave like C++ + pointers. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the character in the QJsonArray of QJsonObject + from which you got the reference. + + \sa operator+() +*/ + +/*! + \fn bool QJsonArray::iterator::operator==(const iterator &other) const + \fn bool QJsonArray::iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QJsonArray::iterator::operator!=(const iterator &other) const + \fn bool QJsonArray::iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool QJsonArray::iterator::operator<(const iterator& other) const + \fn bool QJsonArray::iterator::operator<(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::iterator::operator<=(const iterator& other) const + \fn bool QJsonArray::iterator::operator<=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + or equal to the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::iterator::operator>(const iterator& other) const + \fn bool QJsonArray::iterator::operator>(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::iterator::operator>=(const iterator& other) const + \fn bool QJsonArray::iterator::operator>=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than or equal to the item pointed to by the \a other iterator. +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the + next item in the array and returns an iterator to the new current + item. + + Calling this function on QJsonArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the + next item in the array and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on QJsonArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator--(int) + + \overload + + The postfix -- operator, \c{it--}, makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! \fn int QJsonArray::iterator::operator-(iterator other) const + + Returns the number of items between the item pointed to by \a + other and the item pointed to by this iterator. +*/ + +/*! \class QJsonArray::const_iterator + \inmodule QtCore + \brief The QJsonArray::const_iterator class provides an STL-style const iterator for QJsonArray. + + QJsonArray::const_iterator allows you to iterate over a + QJsonArray. If you want to modify the QJsonArray as + you iterate over it, use QJsonArray::iterator instead. It is generally a + good practice to use QJsonArray::const_iterator on a non-const QJsonArray + as well, unless you need to change the QJsonArray through the + iterator. Const iterators are slightly faster and improves + code readability. + + The default QJsonArray::const_iterator constructor creates an + uninitialized iterator. You must initialize it using a QJsonArray + function like QJsonArray::constBegin(), QJsonArray::constEnd(), or + QJsonArray::insert() before you can start iterating. + + Most QJsonArray functions accept an integer index rather than an + iterator. For that reason, iterators are rarely useful in + connection with QJsonArray. One place where STL-style iterators do + make sense is as arguments to \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be + aware that any non-const function call performed on the QJsonArray + will render all existing iterators undefined. + + \sa QJsonArray::iterator +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called + on an uninitialized iterator. Use operator=() to assign a value + to it before using it. + + \sa QJsonArray::constBegin(), QJsonArray::constEnd() +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator(const QJsonArray *array, int index) + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random access iterator. +*/ + +/*! \typedef QJsonArray::const_iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::value_type + + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::reference + + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::pointer + + \internal +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator(const const_iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator(const iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QJsonValue QJsonArray::const_iterator::operator*() const + + Returns the current item. +*/ + +/*! \fn QJsonValue *QJsonArray::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! \fn QJsonValue QJsonArray::const_iterator::operator[](int j) const + + Returns the item at offset \a j from the item pointed to by this iterator (the item at + position \c{*this + j}). + + This function is provided to make QJsonArray iterators behave like C++ + pointers. + + \sa operator+() +*/ + +/*! \fn bool QJsonArray::const_iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! \fn bool QJsonArray::const_iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator<(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator<=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + or equal to the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator>(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator>=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than or equal to the item pointed to by the \a other iterator. +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the + next item in the array and returns an iterator to the new current + item. + + Calling this function on QJsonArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the + next item in the array and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on QJsonArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator--(int) + + \overload + + The postfix -- operator, \c{it--}, makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! \fn int QJsonArray::const_iterator::operator-(const_iterator other) const + + Returns the number of items between the item pointed to by \a + other and the item pointed to by this iterator. +*/ + + +/*! + \internal + */ +void QJsonArray::detach(uint reserve) +{ + Q_UNUSED(reserve) + Q_ASSERT(!reserve); + detach2(0); +} + +/*! + \internal + */ +bool QJsonArray::detach2(uint reserve) +{ + if (!d) { + if (reserve >= QJsonPrivate::Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return false; + } + d = new QJsonPrivate::Data(reserve, QJsonValue::Array); + a = static_cast<QJsonPrivate::Array *>(d->header->root()); + d->ref.ref(); + return true; + } + if (reserve == 0 && d->ref.load() == 1) + return true; + + QJsonPrivate::Data *x = d->clone(a, reserve); + if (!x) + return false; + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + a = static_cast<QJsonPrivate::Array *>(d->header->root()); + return true; +} + +/*! + \internal + */ +void QJsonArray::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach2(); + d->compact(); + a = static_cast<QJsonPrivate::Array *>(d->header->root()); +} + + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonArray &a) +{ + QDebugStateSaver saver(dbg); + if (!a.a) { + dbg << "QJsonArray()"; + return dbg; + } + QByteArray json; + QJsonPrivate::Writer::arrayToJson(a.a, json, 0, true); + dbg.nospace() << "QJsonArray(" + << json.constData() // print as utf-8 string without extra quotation marks + << ")"; + return dbg; +} +#endif + +QT_END_NAMESPACE + diff --git a/src/corelib/serialization/qjsonarray.h b/src/corelib/serialization/qjsonarray.h new file mode 100644 index 0000000000..8d41138c97 --- /dev/null +++ b/src/corelib/serialization/qjsonarray.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONARRAY_H +#define QJSONARRAY_H + +#include <QtCore/qjsonvalue.h> +#include <QtCore/qiterator.h> +#if defined(Q_COMPILER_INITIALIZER_LISTS) +#include <initializer_list> +#endif + +QT_BEGIN_NAMESPACE + +class QDebug; +class QStringList; +template <typename T> class QList; +typedef QList<QVariant> QVariantList; + +class Q_CORE_EXPORT QJsonArray +{ +public: + QJsonArray(); + +#if defined(Q_COMPILER_INITIALIZER_LISTS) || defined(Q_QDOC) + QJsonArray(std::initializer_list<QJsonValue> args) + { + initialize(); + for (std::initializer_list<QJsonValue>::const_iterator i = args.begin(); i != args.end(); ++i) + append(*i); + } +#endif + + ~QJsonArray(); + + QJsonArray(const QJsonArray &other); + QJsonArray &operator =(const QJsonArray &other); + + QJsonArray(QJsonArray &&other) Q_DECL_NOTHROW + : d(other.d), + a(other.a) + { + other.d = nullptr; + other.a = nullptr; + } + + QJsonArray &operator =(QJsonArray &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + static QJsonArray fromStringList(const QStringList &list); + static QJsonArray fromVariantList(const QVariantList &list); + QVariantList toVariantList() const; + + int size() const; + inline int count() const { return size(); } + + bool isEmpty() const; + QJsonValue at(int i) const; + QJsonValue first() const; + QJsonValue last() const; + + void prepend(const QJsonValue &value); + void append(const QJsonValue &value); + void removeAt(int i); + QJsonValue takeAt(int i); + inline void removeFirst() { removeAt(0); } + inline void removeLast() { removeAt(size() - 1); } + + void insert(int i, const QJsonValue &value); + void replace(int i, const QJsonValue &value); + + bool contains(const QJsonValue &element) const; + QJsonValueRef operator[](int i); + QJsonValue operator[](int i) const; + + bool operator==(const QJsonArray &other) const; + bool operator!=(const QJsonArray &other) const; + + void swap(QJsonArray &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + qSwap(a, other.a); + } + + class const_iterator; + + class iterator { + public: + QJsonArray *a; + int i; + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef QJsonValue value_type; + typedef QJsonValueRef reference; + typedef QJsonValueRefPtr pointer; + + inline iterator() : a(nullptr), i(0) { } + explicit inline iterator(QJsonArray *array, int index) : a(array), i(index) { } + + inline QJsonValueRef operator*() const { return QJsonValueRef(a, i); } +#ifdef Q_QDOC + inline QJsonValueRef* operator->() const; +#else + inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(a, i); } +#endif + inline QJsonValueRef operator[](int j) const { return QJsonValueRef(a, i + j); } + + inline bool operator==(const iterator &o) const { return i == o.i; } + inline bool operator!=(const iterator &o) const { return i != o.i; } + inline bool operator<(const iterator& other) const { return i < other.i; } + inline bool operator<=(const iterator& other) const { return i <= other.i; } + inline bool operator>(const iterator& other) const { return i > other.i; } + inline bool operator>=(const iterator& other) const { return i >= other.i; } + inline bool operator==(const const_iterator &o) const { return i == o.i; } + inline bool operator!=(const const_iterator &o) const { return i != o.i; } + inline bool operator<(const const_iterator& other) const { return i < other.i; } + inline bool operator<=(const const_iterator& other) const { return i <= other.i; } + inline bool operator>(const const_iterator& other) const { return i > other.i; } + inline bool operator>=(const const_iterator& other) const { return i >= other.i; } + inline iterator &operator++() { ++i; return *this; } + inline iterator operator++(int) { iterator n = *this; ++i; return n; } + inline iterator &operator--() { i--; return *this; } + inline iterator operator--(int) { iterator n = *this; i--; return n; } + inline iterator &operator+=(int j) { i+=j; return *this; } + inline iterator &operator-=(int j) { i-=j; return *this; } + inline iterator operator+(int j) const { return iterator(a, i+j); } + inline iterator operator-(int j) const { return iterator(a, i-j); } + inline int operator-(iterator j) const { return i - j.i; } + }; + friend class iterator; + + class const_iterator { + public: + const QJsonArray *a; + int i; + typedef std::random_access_iterator_tag iterator_category; + typedef qptrdiff difference_type; + typedef QJsonValue value_type; + typedef QJsonValue reference; + typedef QJsonValuePtr pointer; + + inline const_iterator() : a(nullptr), i(0) { } + explicit inline const_iterator(const QJsonArray *array, int index) : a(array), i(index) { } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + inline const_iterator(const const_iterator &o) : a(o.a), i(o.i) {} // ### Qt 6: Removed so class can be trivially-copyable +#endif + inline const_iterator(const iterator &o) : a(o.a), i(o.i) {} + + inline QJsonValue operator*() const { return a->at(i); } +#ifdef Q_QDOC + inline QJsonValue* operator->() const; +#else + inline QJsonValuePtr operator->() const { return QJsonValuePtr(a->at(i)); } +#endif + inline QJsonValue operator[](int j) const { return a->at(i+j); } + inline bool operator==(const const_iterator &o) const { return i == o.i; } + inline bool operator!=(const const_iterator &o) const { return i != o.i; } + inline bool operator<(const const_iterator& other) const { return i < other.i; } + inline bool operator<=(const const_iterator& other) const { return i <= other.i; } + inline bool operator>(const const_iterator& other) const { return i > other.i; } + inline bool operator>=(const const_iterator& other) const { return i >= other.i; } + inline const_iterator &operator++() { ++i; return *this; } + inline const_iterator operator++(int) { const_iterator n = *this; ++i; return n; } + inline const_iterator &operator--() { i--; return *this; } + inline const_iterator operator--(int) { const_iterator n = *this; i--; return n; } + inline const_iterator &operator+=(int j) { i+=j; return *this; } + inline const_iterator &operator-=(int j) { i-=j; return *this; } + inline const_iterator operator+(int j) const { return const_iterator(a, i+j); } + inline const_iterator operator-(int j) const { return const_iterator(a, i-j); } + inline int operator-(const_iterator j) const { return i - j.i; } + }; + friend class const_iterator; + + // stl style + inline iterator begin() { detach2(); return iterator(this, 0); } + inline const_iterator begin() const { return const_iterator(this, 0); } + inline const_iterator constBegin() const { return const_iterator(this, 0); } + inline iterator end() { detach2(); return iterator(this, size()); } + inline const_iterator end() const { return const_iterator(this, size()); } + inline const_iterator constEnd() const { return const_iterator(this, size()); } + iterator insert(iterator before, const QJsonValue &value) { insert(before.i, value); return before; } + iterator erase(iterator it) { removeAt(it.i); return it; } + + // more Qt + typedef iterator Iterator; + typedef const_iterator ConstIterator; + + // convenience + inline QJsonArray operator+(const QJsonValue &v) const + { QJsonArray n = *this; n += v; return n; } + inline QJsonArray &operator+=(const QJsonValue &v) + { append(v); return *this; } + inline QJsonArray &operator<< (const QJsonValue &v) + { append(v); return *this; } + + // stl compatibility + inline void push_back(const QJsonValue &t) { append(t); } + inline void push_front(const QJsonValue &t) { prepend(t); } + inline void pop_front() { removeFirst(); } + inline void pop_back() { removeLast(); } + inline bool empty() const { return isEmpty(); } + typedef int size_type; + typedef QJsonValue value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef QJsonValueRef reference; + typedef QJsonValue const_reference; + typedef int difference_type; + +private: + friend class QJsonPrivate::Data; + friend class QJsonValue; + friend class QJsonDocument; + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); + + QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array); + void initialize(); + void compact(); + // ### Qt 6: remove me and merge with detach2 + void detach(uint reserve = 0); + bool detach2(uint reserve = 0); + + QJsonPrivate::Data *d; + QJsonPrivate::Array *a; +}; + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONARRAY_H diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp new file mode 100644 index 0000000000..9794bca60d --- /dev/null +++ b/src/corelib/serialization/qjsondocument.cpp @@ -0,0 +1,667 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qjsondocument.h> +#include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsonarray.h> +#include <qstringlist.h> +#include <qvariant.h> +#include <qdebug.h> +#include "qjsonwriter_p.h" +#include "qjsonparser_p.h" +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! \class QJsonDocument + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonDocument class provides a way to read and write JSON documents. + + QJsonDocument is a class that wraps a complete JSON document and can read and + write this document both from a UTF-8 encoded text based representation as well + as Qt's own binary format. + + A JSON document can be converted from its text-based representation to a QJsonDocument + using QJsonDocument::fromJson(). toJson() converts it back to text. The parser is very + fast and efficient and converts the JSON to the binary representation used by Qt. + + Validity of the parsed document can be queried with !isNull() + + A document can be queried as to whether it contains an array or an object using isArray() + and isObject(). The array or object contained in the document can be retrieved using + array() or object() and then read or manipulated. + + A document can also be created from a stored binary representation using fromBinaryData() or + fromRawData(). + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + * Constructs an empty and invalid document. + */ +QJsonDocument::QJsonDocument() + : d(0) +{ +} + +/*! + * Creates a QJsonDocument from \a object. + */ +QJsonDocument::QJsonDocument(const QJsonObject &object) + : d(0) +{ + setObject(object); +} + +/*! + * Constructs a QJsonDocument from \a array. + */ +QJsonDocument::QJsonDocument(const QJsonArray &array) + : d(0) +{ + setArray(array); +} + +/*! + \internal + */ +QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) + : d(data) +{ + Q_ASSERT(d); + d->ref.ref(); +} + +/*! + Deletes the document. + + Binary data set with fromRawData is not freed. + */ +QJsonDocument::~QJsonDocument() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + * Creates a copy of the \a other document. + */ +QJsonDocument::QJsonDocument(const QJsonDocument &other) +{ + d = other.d; + if (d) + d->ref.ref(); +} + +/*! + * Assigns the \a other document to this QJsonDocument. + * Returns a reference to this object. + */ +QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + + return *this; +} + +/*! + \fn QJsonDocument::QJsonDocument(QJsonDocument &&other) + \since 5.10 + + Move-constructs a QJsonDocument from \a other. +*/ + +/*! + \fn QJsonDocument &QJsonDocument::operator =(QJsonDocument &&other) + \since 5.10 + + Move-assigns \a other to this document. +*/ + +/*! + \fn void QJsonDocument::swap(QJsonDocument &other) + \since 5.10 + + Swaps the document \a other with this. This operation is very fast and never fails. +*/ + + +/*! \enum QJsonDocument::DataValidation + + This value is used to tell QJsonDocument whether to validate the binary data + when converting to a QJsonDocument using fromBinaryData() or fromRawData(). + + \value Validate Validate the data before using it. This is the default. + \value BypassValidation Bypasses data validation. Only use if you received the + data from a trusted place and know it's valid, as using of invalid data can crash + the application. + */ + +/*! + Creates a QJsonDocument that uses the first \a size bytes from + \a data. It assumes \a data contains a binary encoded JSON document. + The created document does not take ownership of \a data and the caller + has to guarantee that \a data will not be deleted or modified as long as + any QJsonDocument, QJsonObject or QJsonArray still references the data. + + \a data has to be aligned to a 4 byte boundary. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + Returns a QJsonDocument representing the data. + + \sa rawData(), fromBinaryData(), isNull(), DataValidation + */ +QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) +{ + if (quintptr(data) & 3) { + qWarning("QJsonDocument::fromRawData: data has to have 4 byte alignment"); + return QJsonDocument(); + } + + QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size); + d->ownsData = false; + + if (validation != BypassValidation && !d->valid()) { + delete d; + return QJsonDocument(); + } + + return QJsonDocument(d); +} + +/*! + Returns the raw binary representation of the data + \a size will contain the size of the returned data. + + This method is useful to e.g. stream the JSON document + in it's binary form to a file. + */ +const char *QJsonDocument::rawData(int *size) const +{ + if (!d) { + *size = 0; + return 0; + } + *size = d->alloc; + return d->rawData; +} + +/*! + Creates a QJsonDocument from \a data. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + \sa toBinaryData(), fromRawData(), isNull(), DataValidation + */ +QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) +{ + if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) + return QJsonDocument(); + + QJsonPrivate::Header h; + memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header)); + QJsonPrivate::Base root; + memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base)); + + // do basic checks here, so we don't try to allocate more memory than we can. + if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u || + sizeof(QJsonPrivate::Header) + root.size > (uint)data.size()) + return QJsonDocument(); + + const uint size = sizeof(QJsonPrivate::Header) + root.size; + char *raw = (char *)malloc(size); + if (!raw) + return QJsonDocument(); + + memcpy(raw, data.constData(), size); + QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size); + + if (validation != BypassValidation && !d->valid()) { + delete d; + return QJsonDocument(); + } + + return QJsonDocument(d); +} + +/*! + Creates a QJsonDocument from the QVariant \a variant. + + If the \a variant contains any other type than a QVariantMap, + QVariantHash, QVariantList or QStringList, the returned document is invalid. + + \sa toVariant() + */ +QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) +{ + QJsonDocument doc; + switch (variant.type()) { + case QVariant::Map: + doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); + break; + case QVariant::Hash: + doc.setObject(QJsonObject::fromVariantHash(variant.toHash())); + break; + case QVariant::List: + doc.setArray(QJsonArray::fromVariantList(variant.toList())); + break; + case QVariant::StringList: + doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + break; + default: + break; + } + return doc; +} + +/*! + Returns a QVariant representing the Json document. + + The returned variant will be a QVariantList if the document is + a QJsonArray and a QVariantMap if the document is a QJsonObject. + + \sa fromVariant(), QJsonValue::toVariant() + */ +QVariant QJsonDocument::toVariant() const +{ + if (!d) + return QVariant(); + + if (d->header->root()->isArray()) + return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root())).toVariantList(); + else + return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root())).toVariantMap(); +} + +/*! + Converts the QJsonDocument to a UTF-8 encoded JSON document. + + \sa fromJson() + */ +#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) +QByteArray QJsonDocument::toJson() const +{ + return toJson(Indented); +} +#endif + +/*! + \enum QJsonDocument::JsonFormat + + This value defines the format of the JSON byte array produced + when converting to a QJsonDocument using toJson(). + + \value Indented Defines human readable output as follows: + \code + { + "Array": [ + true, + 999, + "string" + ], + "Key": "Value", + "null": null + } + \endcode + + \value Compact Defines a compact output as follows: + \code + {"Array":[true,999,"string"],"Key":"Value","null":null} + \endcode + */ + +/*! + Converts the QJsonDocument to a UTF-8 encoded JSON document in the provided \a format. + + \sa fromJson(), JsonFormat + */ +#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) +QByteArray QJsonDocument::toJson(JsonFormat format) const +{ + QByteArray json; + if (!d) + return json; + + if (d->header->root()->isArray()) + QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(d->header->root()), json, 0, (format == Compact)); + else + QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(d->header->root()), json, 0, (format == Compact)); + + return json; +} +#endif + +/*! + Parses \a json as a UTF-8 encoded JSON document, and creates a QJsonDocument + from it. + + Returns a valid (non-null) QJsonDocument if the parsing succeeds. If it fails, + the returned document will be null, and the optional \a error variable will contain + further details about the error. + + \sa toJson(), QJsonParseError, isNull() + */ +QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) +{ + QJsonPrivate::Parser parser(json.constData(), json.length()); + return parser.parse(error); +} + +/*! + Returns \c true if the document doesn't contain any data. + */ +bool QJsonDocument::isEmpty() const +{ + if (!d) + return true; + + return false; +} + +/*! + Returns a binary representation of the document. + + The binary representation is also the native format used internally in Qt, + and is very efficient and fast to convert to and from. + + The binary format can be stored on disk and interchanged with other applications + or computers. fromBinaryData() can be used to convert it back into a + JSON document. + + \sa fromBinaryData() + */ +QByteArray QJsonDocument::toBinaryData() const +{ + if (!d || !d->rawData) + return QByteArray(); + + return QByteArray(d->rawData, d->header->root()->size + sizeof(QJsonPrivate::Header)); +} + +/*! + Returns \c true if the document contains an array. + + \sa array(), isObject() + */ +bool QJsonDocument::isArray() const +{ + if (!d) + return false; + + QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; + return h->root()->isArray(); +} + +/*! + Returns \c true if the document contains an object. + + \sa object(), isArray() + */ +bool QJsonDocument::isObject() const +{ + if (!d) + return false; + + QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; + return h->root()->isObject(); +} + +/*! + Returns the QJsonObject contained in the document. + + Returns an empty object if the document contains an + array. + + \sa isObject(), array(), setObject() + */ +QJsonObject QJsonDocument::object() const +{ + if (d) { + QJsonPrivate::Base *b = d->header->root(); + if (b->isObject()) + return QJsonObject(d, static_cast<QJsonPrivate::Object *>(b)); + } + return QJsonObject(); +} + +/*! + Returns the QJsonArray contained in the document. + + Returns an empty array if the document contains an + object. + + \sa isArray(), object(), setArray() + */ +QJsonArray QJsonDocument::array() const +{ + if (d) { + QJsonPrivate::Base *b = d->header->root(); + if (b->isArray()) + return QJsonArray(d, static_cast<QJsonPrivate::Array *>(b)); + } + return QJsonArray(); +} + +/*! + Sets \a object as the main object of this document. + + \sa setArray(), object() + */ +void QJsonDocument::setObject(const QJsonObject &object) +{ + if (d && !d->ref.deref()) + delete d; + + d = object.d; + + if (!d) { + d = new QJsonPrivate::Data(0, QJsonValue::Object); + } else if (d->compactionCounter || object.o != d->header->root()) { + QJsonObject o(object); + if (d->compactionCounter) + o.compact(); + else + o.detach2(); + d = o.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Sets \a array as the main object of this document. + + \sa setObject(), array() + */ +void QJsonDocument::setArray(const QJsonArray &array) +{ + if (d && !d->ref.deref()) + delete d; + + d = array.d; + + if (!d) { + d = new QJsonPrivate::Data(0, QJsonValue::Array); + } else if (d->compactionCounter || array.a != d->header->root()) { + QJsonArray a(array); + if (d->compactionCounter) + a.compact(); + else + a.detach2(); + d = a.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + Equivalent to calling object().value(key). + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist, + or if isObject() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject + */ +const QJsonValue QJsonDocument::operator[](const QString &key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return object().value(key); +} + +/*! + \overload + \since 5.10 +*/ +const QJsonValue QJsonDocument::operator[](QLatin1String key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return object().value(key); +} + +/*! + Returns a QJsonValue representing the value for index \a i. + + Equivalent to calling array().at(i). + + The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds, + or if isArray() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray + */ +const QJsonValue QJsonDocument::operator[](int i) const +{ + if (!isArray()) + return QJsonValue(QJsonValue::Undefined); + + return array().at(i); +} + +/*! + Returns \c true if the \a other document is equal to this document. + */ +bool QJsonDocument::operator==(const QJsonDocument &other) const +{ + if (d == other.d) + return true; + + if (!d || !other.d) + return false; + + if (d->header->root()->isArray() != other.d->header->root()->isArray()) + return false; + + if (d->header->root()->isObject()) + return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root())) + == QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(other.d->header->root())); + else + return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root())) + == QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(other.d->header->root())); +} + +/*! + \fn bool QJsonDocument::operator!=(const QJsonDocument &other) const + + returns \c true if \a other is not equal to this document + */ + +/*! + returns \c true if this document is null. + + Null documents are documents created through the default constructor. + + Documents created from UTF-8 encoded text or the binary format are + validated during parsing. If validation fails, the returned document + will also be null. + */ +bool QJsonDocument::isNull() const +{ + return (d == 0); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonDocument &o) +{ + QDebugStateSaver saver(dbg); + if (!o.d) { + dbg << "QJsonDocument()"; + return dbg; + } + QByteArray json; + if (o.d->header->root()->isArray()) + QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(o.d->header->root()), json, 0, true); + else + QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(o.d->header->root()), json, 0, true); + dbg.nospace() << "QJsonDocument(" + << json.constData() // print as utf-8 string without extra quotation marks + << ')'; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsondocument.h b/src/corelib/serialization/qjsondocument.h new file mode 100644 index 0000000000..b784890c54 --- /dev/null +++ b/src/corelib/serialization/qjsondocument.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONDOCUMENT_H +#define QJSONDOCUMENT_H + +#include <QtCore/qjsonvalue.h> + +QT_BEGIN_NAMESPACE + +class QDebug; + +namespace QJsonPrivate { + class Parser; +} + +struct Q_CORE_EXPORT QJsonParseError +{ + enum ParseError { + NoError = 0, + UnterminatedObject, + MissingNameSeparator, + UnterminatedArray, + MissingValueSeparator, + IllegalValue, + TerminationByNumber, + IllegalNumber, + IllegalEscapeSequence, + IllegalUTF8String, + UnterminatedString, + MissingObject, + DeepNesting, + DocumentTooLarge, + GarbageAtEnd + }; + + QString errorString() const; + + int offset; + ParseError error; +}; + +class Q_CORE_EXPORT QJsonDocument +{ +public: +#ifdef Q_LITTLE_ENDIAN + static const uint BinaryFormatTag = ('q') | ('b' << 8) | ('j' << 16) | ('s' << 24); +#else + static const uint BinaryFormatTag = ('q' << 24) | ('b' << 16) | ('j' << 8) | ('s'); +#endif + + QJsonDocument(); + explicit QJsonDocument(const QJsonObject &object); + explicit QJsonDocument(const QJsonArray &array); + ~QJsonDocument(); + + QJsonDocument(const QJsonDocument &other); + QJsonDocument &operator =(const QJsonDocument &other); + + QJsonDocument(QJsonDocument &&other) Q_DECL_NOTHROW + : d(other.d) + { + other.d = nullptr; + } + + QJsonDocument &operator =(QJsonDocument &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + void swap(QJsonDocument &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + } + + enum DataValidation { + Validate, + BypassValidation + }; + + static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); + const char *rawData(int *size) const; + + static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); + QByteArray toBinaryData() const; + + static QJsonDocument fromVariant(const QVariant &variant); + QVariant toVariant() const; + + enum JsonFormat { + Indented, + Compact + }; + + static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = nullptr); + +#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) + QByteArray toJson() const; //### Merge in Qt6 + QByteArray toJson(JsonFormat format) const; +#endif + + bool isEmpty() const; + bool isArray() const; + bool isObject() const; + + QJsonObject object() const; + QJsonArray array() const; + + void setObject(const QJsonObject &object); + void setArray(const QJsonArray &array); + + const QJsonValue operator[](const QString &key) const; + const QJsonValue operator[](QLatin1String key) const; + const QJsonValue operator[](int i) const; + + bool operator==(const QJsonDocument &other) const; + bool operator!=(const QJsonDocument &other) const { return !(*this == other); } + + bool isNull() const; + +private: + friend class QJsonValue; + friend class QJsonPrivate::Data; + friend class QJsonPrivate::Parser; + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); + + QJsonDocument(QJsonPrivate::Data *data); + + QJsonPrivate::Data *d; +}; + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONDOCUMENT_H diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp new file mode 100644 index 0000000000..4a316c8a6f --- /dev/null +++ b/src/corelib/serialization/qjsonobject.cpp @@ -0,0 +1,1312 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsonarray.h> +#include <qstringlist.h> +#include <qdebug.h> +#include <qvariant.h> +#include "qjson_p.h" +#include "qjsonwriter_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJsonObject + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonObject class encapsulates a JSON object. + + A JSON object is a list of key value pairs, where the keys are unique strings + and the values are represented by a QJsonValue. + + A QJsonObject can be converted to and from a QVariantMap. You can query the + number of (key, value) pairs with size(), insert(), and remove() entries from it + and iterate over its content using the standard C++ iterator pattern. + + QJsonObject is an implicitly shared class, and shares the data with the document + it has been created from as long as it is not being modified. + + You can convert the object to and from text based JSON through QJsonDocument. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \typedef QJsonObject::Iterator + + Qt-style synonym for QJsonObject::iterator. +*/ + +/*! + \typedef QJsonObject::ConstIterator + + Qt-style synonym for QJsonObject::const_iterator. +*/ + +/*! + \typedef QJsonObject::key_type + + Typedef for QString. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonObject::mapped_type + + Typedef for QJsonValue. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonObject::size_type + + Typedef for int. Provided for STL compatibility. +*/ + + +/*! + Constructs an empty JSON object. + + \sa isEmpty() + */ +QJsonObject::QJsonObject() + : d(0), o(0) +{ +} + +/*! + \fn QJsonObject::QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args) + \since 5.4 + Constructs a QJsonObject instance initialized from \a args initialization list. + For example: + \code + QJsonObject object + { + {"property1", 1}, + {"property2", 2} + }; + \endcode +*/ + +/*! + \internal + */ +QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) + : d(data), o(object) +{ + Q_ASSERT(d); + Q_ASSERT(o); + d->ref.ref(); +} + +/*! + This method replaces part of the QJsonObject(std::initializer_list<QPair<QString, QJsonValue>> args) body. + The constructor needs to be inline, but we do not want to leak implementation details + of this class. + \note this method is called for an uninitialized object + \internal + */ + +void QJsonObject::initialize() +{ + d = 0; + o = 0; +} + +/*! + Destroys the object. + */ +QJsonObject::~QJsonObject() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + + Since QJsonObject is implicitly shared, the copy is shallow + as long as the object does not get modified. + */ +QJsonObject::QJsonObject(const QJsonObject &other) +{ + d = other.d; + o = other.o; + if (d) + d->ref.ref(); +} + +/*! + Assigns \a other to this object. + */ +QJsonObject &QJsonObject::operator =(const QJsonObject &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + o = other.o; + + return *this; +} + +/*! + \fn QJsonObject::QJsonObject(QJsonObject &&other) + \since 5.10 + + Move-constructs a QJsonObject from \a other. +*/ + +/*! + \fn QJsonObject &QJsonObject::operator =(QJsonObject &&other) + \since 5.10 + + Move-assigns \a other to this object. +*/ + +/*! + \fn void QJsonObject::swap(QJsonObject &other) + \since 5.10 + + Swaps the object \a other with this. This operation is very fast and never fails. +*/ + + +/*! + Converts the variant map \a map to a QJsonObject. + + The keys in \a map will be used as the keys in the JSON object, + and the QVariant values will be converted to JSON values. + + \sa fromVariantHash(), toVariantMap(), QJsonValue::fromVariant() + */ +QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) +{ + QJsonObject object; + if (map.isEmpty()) + return object; + + object.detach2(1024); + + QVector<QJsonPrivate::offset> offsets; + QJsonPrivate::offset currentOffset; + currentOffset = sizeof(QJsonPrivate::Base); + + // the map is already sorted, so we can simply append one entry after the other and + // write the offset table at the end + for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { + QString key = it.key(); + QJsonValue val = QJsonValue::fromVariant(it.value()); + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + bool latinKey = QJsonPrivate::useCompressed(key); + int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); + int requiredSize = valueOffset + valueSize; + + if (!object.detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry + return QJsonObject(); + + QJsonPrivate::Entry *e = reinterpret_cast<QJsonPrivate::Entry *>(reinterpret_cast<char *>(object.o) + currentOffset); + e->value.type = val.t; + e->value.latinKey = latinKey; + e->value.latinOrIntValue = latinOrIntValue; + e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)object.o + valueOffset); + QJsonPrivate::copyString((char *)(e + 1), key, latinKey); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); + + offsets << currentOffset; + currentOffset += requiredSize; + object.o->size = currentOffset; + } + + // write table + object.o->tableOffset = currentOffset; + if (!object.detach2(sizeof(QJsonPrivate::offset)*offsets.size())) + return QJsonObject(); + memcpy(object.o->table(), offsets.constData(), offsets.size()*sizeof(uint)); + object.o->length = offsets.size(); + object.o->size = currentOffset + sizeof(QJsonPrivate::offset)*offsets.size(); + + return object; +} + +/*! + Converts this object to a QVariantMap. + + Returns the created map. + + \sa toVariantHash() + */ +QVariantMap QJsonObject::toVariantMap() const +{ + QVariantMap map; + if (o) { + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + map.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); + } + } + return map; +} + +/*! + Converts the variant hash \a hash to a QJsonObject. + \since 5.5 + + The keys in \a hash will be used as the keys in the JSON object, + and the QVariant values will be converted to JSON values. + + \sa fromVariantMap(), toVariantHash(), QJsonValue::fromVariant() + */ +QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) +{ + // ### this is implemented the trivial way, not the most efficient way + + QJsonObject object; + for (QVariantHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) + object.insert(it.key(), QJsonValue::fromVariant(it.value())); + return object; +} + +/*! + Converts this object to a QVariantHash. + \since 5.5 + + Returns the created hash. + + \sa toVariantMap() + */ +QVariantHash QJsonObject::toVariantHash() const +{ + QVariantHash hash; + if (o) { + hash.reserve(o->length); + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + hash.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); + } + } + return hash; +} + +/*! + Returns a list of all keys in this object. + + The list is sorted lexographically. + */ +QStringList QJsonObject::keys() const +{ + QStringList keys; + if (o) { + keys.reserve(o->length); + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + keys.append(e->key()); + } + } + return keys; +} + +/*! + Returns the number of (key, value) pairs stored in the object. + */ +int QJsonObject::size() const +{ + if (!d) + return 0; + + return o->length; +} + +/*! + Returns \c true if the object is empty. This is the same as size() == 0. + + \sa size() + */ +bool QJsonObject::isEmpty() const +{ + if (!d) + return true; + + return !o->length; +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist. + + \sa QJsonValue, QJsonValue::isUndefined() + */ +QJsonValue QJsonObject::value(const QString &key) const +{ + if (!d) + return QJsonValue(QJsonValue::Undefined); + + bool keyExists; + int i = o->indexOf(key, &keyExists); + if (!keyExists) + return QJsonValue(QJsonValue::Undefined); + return QJsonValue(d, o, o->entryAt(i)->value); +} + +/*! + \overload + \since 5.7 +*/ +QJsonValue QJsonObject::value(QLatin1String key) const +{ + if (!d) + return QJsonValue(QJsonValue::Undefined); + + bool keyExists; + int i = o->indexOf(key, &keyExists); + if (!keyExists) + return QJsonValue(QJsonValue::Undefined); + return QJsonValue(d, o, o->entryAt(i)->value); +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + This does the same as value(). + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist. + + \sa value(), QJsonValue, QJsonValue::isUndefined() + */ +QJsonValue QJsonObject::operator [](const QString &key) const +{ + return value(key); +} + +/*! + \fn QJsonValue QJsonObject::operator [](QLatin1String key) const + + \overload + \since 5.7 +*/ + +/*! + Returns a reference to the value for \a key. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + \sa value() + */ +QJsonValueRef QJsonObject::operator [](const QString &key) +{ + // ### somewhat inefficient, as we lookup the key twice if it doesn't yet exist + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : -1; + if (!keyExists) { + iterator i = insert(key, QJsonValue()); + index = i.i; + } + return QJsonValueRef(this, index); +} + +/*! + \overload + \since 5.7 +*/ +QJsonValueRef QJsonObject::operator [](QLatin1String key) +{ + // ### optimize me + return operator[](QString(key)); +} + +/*! + Inserts a new item with the key \a key and a value of \a value. + + If there is already an item with the key \a key, then that item's value + is replaced with \a value. + + Returns an iterator pointing to the inserted item. + + If the value is QJsonValue::Undefined, it will cause the key to get removed + from the object. The returned iterator will then point to end(). + + \sa remove(), take(), QJsonObject::iterator, end() + */ +QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value) +{ + if (value.t == QJsonValue::Undefined) { + remove(key); + return end(); + } + QJsonValue val = value; + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + bool latinKey = QJsonPrivate::useCompressed(key); + int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); + int requiredSize = valueOffset + valueSize; + + if (!detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry + return iterator(); + + if (!o->length) + o->tableOffset = sizeof(QJsonPrivate::Object); + + bool keyExists = false; + int pos = o->indexOf(key, &keyExists); + if (keyExists) + ++d->compactionCounter; + + uint off = o->reserveSpace(requiredSize, pos, 1, keyExists); + if (!off) + return end(); + + QJsonPrivate::Entry *e = o->entryAt(pos); + e->value.type = val.t; + e->value.latinKey = latinKey; + e->value.latinOrIntValue = latinOrIntValue; + e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)o + valueOffset); + QJsonPrivate::copyString((char *)(e + 1), key, latinKey); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); + + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + return iterator(this, pos); +} + +/*! + Removes \a key from the object. + + \sa insert(), take() + */ +void QJsonObject::remove(const QString &key) +{ + if (!d) + return; + + bool keyExists; + int index = o->indexOf(key, &keyExists); + if (!keyExists) + return; + + detach2(); + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); +} + +/*! + Removes \a key from the object. + + Returns a QJsonValue containing the value referenced by \a key. + If \a key was not contained in the object, the returned QJsonValue + is QJsonValue::Undefined. + + \sa insert(), remove(), QJsonValue + */ +QJsonValue QJsonObject::take(const QString &key) +{ + if (!o) + return QJsonValue(QJsonValue::Undefined); + + bool keyExists; + int index = o->indexOf(key, &keyExists); + if (!keyExists) + return QJsonValue(QJsonValue::Undefined); + + QJsonValue v(d, o, o->entryAt(index)->value); + detach2(); + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + return v; +} + +/*! + Returns \c true if the object contains key \a key. + + \sa insert(), remove(), take() + */ +bool QJsonObject::contains(const QString &key) const +{ + if (!o) + return false; + + bool keyExists; + o->indexOf(key, &keyExists); + return keyExists; +} + +/*! + \overload + \since 5.7 +*/ +bool QJsonObject::contains(QLatin1String key) const +{ + if (!o) + return false; + + bool keyExists; + o->indexOf(key, &keyExists); + return keyExists; +} + +/*! + Returns \c true if \a other is equal to this object. + */ +bool QJsonObject::operator==(const QJsonObject &other) const +{ + if (o == other.o) + return true; + + if (!o) + return !other.o->length; + if (!other.o) + return !o->length; + if (o->length != other.o->length) + return false; + + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + QJsonValue v(d, o, e->value); + if (other.value(e->key()) != v) + return false; + } + + return true; +} + +/*! + Returns \c true if \a other is not equal to this object. + */ +bool QJsonObject::operator!=(const QJsonObject &other) const +{ + return !(*this == other); +} + +/*! + Removes the (key, value) pair pointed to by the iterator \a it + from the map, and returns an iterator to the next item in the + map. + + \sa remove() + */ +QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it) +{ + Q_ASSERT(d && d->ref.load() == 1); + if (it.o != this || it.i < 0 || it.i >= (int)o->length) + return iterator(this, o->length); + + int index = it.i; + + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + // iterator hasn't changed + return it; +} + +/*! + Returns an iterator pointing to the item with key \a key in the + map. + + If the map contains no item with key \a key, the function + returns end(). + */ +QJsonObject::iterator QJsonObject::find(const QString &key) +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + detach2(); + return iterator(this, index); +} + +/*! + \overload + \since 5.7 +*/ +QJsonObject::iterator QJsonObject::find(QLatin1String key) +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + detach2(); + return iterator(this, index); +} + +/*! \fn QJsonObject::const_iterator QJsonObject::find(const QString &key) const + + \overload +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::find(QLatin1String key) const + + \overload + \since 5.7 +*/ + +/*! + Returns a const iterator pointing to the item with key \a key in the + map. + + If the map contains no item with key \a key, the function + returns constEnd(). + */ +QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + return const_iterator(this, index); +} + +/*! + \overload + \since 5.7 +*/ +QJsonObject::const_iterator QJsonObject::constFind(QLatin1String key) const +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + return const_iterator(this, index); +} + +/*! \fn int QJsonObject::count() const + + \overload + + Same as size(). +*/ + +/*! \fn int QJsonObject::length() const + + \overload + + Same as size(). +*/ + +/*! \fn QJsonObject::iterator QJsonObject::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the object. + + \sa constBegin(), end() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::begin() const + + \overload +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the object. + + \sa begin(), constEnd() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the object. + + \sa begin(), constEnd() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::end() const + + \overload +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the object. + + \sa constBegin(), end() +*/ + +/*! + \fn bool QJsonObject::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty(), returning \c true if the object is empty; otherwise + returning \c false. +*/ + +/*! \class QJsonObject::iterator + \inmodule QtCore + \ingroup json + \reentrant + \since 5.0 + + \brief The QJsonObject::iterator class provides an STL-style non-const iterator for QJsonObject. + + QJsonObject::iterator allows you to iterate over a QJsonObject + and to modify the value (but not the key) stored under + a particular key. If you want to iterate over a const QJsonObject, you + should use QJsonObject::const_iterator. It is generally good practice to + use QJsonObject::const_iterator on a non-const QJsonObject as well, unless you + need to change the QJsonObject through the iterator. Const iterators are + slightly faster, and improve code readability. + + The default QJsonObject::iterator constructor creates an uninitialized + iterator. You must initialize it using a QJsonObject function like + QJsonObject::begin(), QJsonObject::end(), or QJsonObject::find() before you can + start iterating. + + Multiple iterators can be used on the same object. Existing iterators will however + become dangling once the object gets modified. + + \sa QJsonObject::const_iterator, {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! \typedef QJsonObject::iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonObject::iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random-access iterator. + + \note In Qt versions before 5.6, this was set by mistake to + \e {std::bidirectional_iterator_tag}. +*/ + +/*! \typedef QJsonObject::iterator::reference + + \internal +*/ + +/*! \typedef QJsonObject::iterator::value_type + + \internal +*/ + +/*! \typedef QJsonObject::iterator::pointer + + \internal +*/ + +/*! \fn QJsonObject::iterator::iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa QJsonObject::begin(), QJsonObject::end() +*/ + +/*! \fn QJsonObject::iterator::iterator(QJsonObject *obj, int index) + \internal +*/ + +/*! \fn QString QJsonObject::iterator::key() const + + Returns the current item's key. + + There is no direct way of changing an item's key through an + iterator, although it can be done by calling QJsonObject::erase() + followed by QJsonObject::insert(). + + \sa value() +*/ + +/*! \fn QJsonValueRef QJsonObject::iterator::value() const + + Returns a modifiable reference to the current item's value. + + You can change the value of an item by using value() on + the left side of an assignment. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + \sa key(), operator*() +*/ + +/*! \fn QJsonValueRef QJsonObject::iterator::operator*() const + + Returns a modifiable reference to the current item's value. + + Same as value(). + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + \sa key() +*/ + +/*! \fn QJsonValueRef *QJsonObject::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! + \fn bool QJsonObject::iterator::operator==(const iterator &other) const + \fn bool QJsonObject::iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QJsonObject::iterator::operator!=(const iterator &other) const + \fn bool QJsonObject::iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the + next item in the object and returns an iterator to the new current + item. + + Calling this function on QJsonObject::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the + next item in the object and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item + current and returns an iterator pointing to the new current item. + + Calling this function on QJsonObject::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator--(int) + + \overload + + The postfix -- operator, \c{i--}, makes the preceding item + current and returns an iterator pointing to the previously + current item. +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-() + +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+() +*/ + +/*! \fn QJsonObject::iterator &QJsonObject::iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonObject::iterator &QJsonObject::iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! + \class QJsonObject::const_iterator + \inmodule QtCore + \ingroup json + \since 5.0 + \brief The QJsonObject::const_iterator class provides an STL-style const iterator for QJsonObject. + + QJsonObject::const_iterator allows you to iterate over a QJsonObject. + If you want to modify the QJsonObject as you iterate + over it, you must use QJsonObject::iterator instead. It is generally + good practice to use QJsonObject::const_iterator on a non-const QJsonObject as + well, unless you need to change the QJsonObject through the iterator. + Const iterators are slightly faster and improve code + readability. + + The default QJsonObject::const_iterator constructor creates an + uninitialized iterator. You must initialize it using a QJsonObject + function like QJsonObject::constBegin(), QJsonObject::constEnd(), or + QJsonObject::find() before you can start iterating. + + Multiple iterators can be used on the same object. Existing iterators + will however become dangling if the object gets modified. + + \sa QJsonObject::iterator, {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! \typedef QJsonObject::const_iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonObject::const_iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random-access iterator. + + \note In Qt versions before 5.6, this was set by mistake to + \e {std::bidirectional_iterator_tag}. +*/ + +/*! \typedef QJsonObject::const_iterator::reference + + \internal +*/ + +/*! \typedef QJsonObject::const_iterator::value_type + + \internal +*/ + +/*! \typedef QJsonObject::const_iterator::pointer + + \internal +*/ + +/*! \fn QJsonObject::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa QJsonObject::constBegin(), QJsonObject::constEnd() +*/ + +/*! \fn QJsonObject::const_iterator::const_iterator(const QJsonObject *obj, int index) + \internal +*/ + +/*! \fn QJsonObject::const_iterator::const_iterator(const iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QString QJsonObject::const_iterator::key() const + + Returns the current item's key. + + \sa value() +*/ + +/*! \fn QJsonValue QJsonObject::const_iterator::value() const + + Returns the current item's value. + + \sa key(), operator*() +*/ + +/*! \fn QJsonValue QJsonObject::const_iterator::operator*() const + + Returns the current item's value. + + Same as value(). + + \sa key() +*/ + +/*! \fn QJsonValue *QJsonObject::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! \fn bool QJsonObject::const_iterator::operator==(const const_iterator &other) const + \fn bool QJsonObject::const_iterator::operator==(const iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! \fn bool QJsonObject::const_iterator::operator!=(const const_iterator &other) const + \fn bool QJsonObject::const_iterator::operator!=(const iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the + next item in the object and returns an iterator to the new current + item. + + Calling this function on QJsonObject::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the + next item in the object and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item + current and returns an iterator pointing to the new current item. + + Calling this function on QJsonObject::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator--(int) + + \overload + + The postfix -- operator, \c{i--}, makes the preceding item + current and returns an iterator pointing to the previously + current item. +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + This operation can be slow for large \a j values. + + \sa operator-() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + This operation can be slow for large \a j values. + + \sa operator+() +*/ + +/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + This operation can be slow for large \a j values. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + This operation can be slow for large \a j values. + + \sa operator+=(), operator-() +*/ + + +/*! + \internal + */ +void QJsonObject::detach(uint reserve) +{ + Q_UNUSED(reserve) + Q_ASSERT(!reserve); + detach2(reserve); +} + +bool QJsonObject::detach2(uint reserve) +{ + if (!d) { + if (reserve >= QJsonPrivate::Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return false; + } + d = new QJsonPrivate::Data(reserve, QJsonValue::Object); + o = static_cast<QJsonPrivate::Object *>(d->header->root()); + d->ref.ref(); + return true; + } + if (reserve == 0 && d->ref.load() == 1) + return true; + + QJsonPrivate::Data *x = d->clone(o, reserve); + if (!x) + return false; + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + o = static_cast<QJsonPrivate::Object *>(d->header->root()); + return true; +} + +/*! + \internal + */ +void QJsonObject::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach2(); + d->compact(); + o = static_cast<QJsonPrivate::Object *>(d->header->root()); +} + +/*! + \internal + */ +QString QJsonObject::keyAt(int i) const +{ + Q_ASSERT(o && i >= 0 && i < (int)o->length); + + QJsonPrivate::Entry *e = o->entryAt(i); + return e->key(); +} + +/*! + \internal + */ +QJsonValue QJsonObject::valueAt(int i) const +{ + if (!o || i < 0 || i >= (int)o->length) + return QJsonValue(QJsonValue::Undefined); + + QJsonPrivate::Entry *e = o->entryAt(i); + return QJsonValue(d, o, e->value); +} + +/*! + \internal + */ +void QJsonObject::setValueAt(int i, const QJsonValue &val) +{ + Q_ASSERT(o && i >= 0 && i < (int)o->length); + + QJsonPrivate::Entry *e = o->entryAt(i); + insert(e->key(), val); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonObject &o) +{ + QDebugStateSaver saver(dbg); + if (!o.o) { + dbg << "QJsonObject()"; + return dbg; + } + QByteArray json; + QJsonPrivate::Writer::objectToJson(o.o, json, 0, true); + dbg.nospace() << "QJsonObject(" + << json.constData() // print as utf-8 string without extra quotation marks + << ")"; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonobject.h b/src/corelib/serialization/qjsonobject.h new file mode 100644 index 0000000000..610bce694c --- /dev/null +++ b/src/corelib/serialization/qjsonobject.h @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONOBJECT_H +#define QJSONOBJECT_H + +#include <QtCore/qjsonvalue.h> +#include <QtCore/qiterator.h> +#ifdef Q_COMPILER_INITIALIZER_LISTS +#include <QtCore/qpair.h> +#include <initializer_list> +#endif + +QT_BEGIN_NAMESPACE + +class QDebug; +template <class Key, class T> class QMap; +typedef QMap<QString, QVariant> QVariantMap; +template <class Key, class T> class QHash; +typedef QHash<QString, QVariant> QVariantHash; + +class Q_CORE_EXPORT QJsonObject +{ +public: + QJsonObject(); + +#if defined(Q_COMPILER_INITIALIZER_LISTS) || defined(Q_QDOC) + QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args) + { + initialize(); + for (std::initializer_list<QPair<QString, QJsonValue> >::const_iterator i = args.begin(); i != args.end(); ++i) + insert(i->first, i->second); + } +#endif + + ~QJsonObject(); + + QJsonObject(const QJsonObject &other); + QJsonObject &operator =(const QJsonObject &other); + + QJsonObject(QJsonObject &&other) Q_DECL_NOTHROW + : d(other.d), o(other.o) + { + other.d = nullptr; + other.o = nullptr; + } + + QJsonObject &operator =(QJsonObject &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + void swap(QJsonObject &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + qSwap(o, other.o); + } + + static QJsonObject fromVariantMap(const QVariantMap &map); + QVariantMap toVariantMap() const; + static QJsonObject fromVariantHash(const QVariantHash &map); + QVariantHash toVariantHash() const; + + QStringList keys() const; + int size() const; + inline int count() const { return size(); } + inline int length() const { return size(); } + bool isEmpty() const; + + QJsonValue value(const QString &key) const; + QJsonValue value(QLatin1String key) const; + QJsonValue operator[] (const QString &key) const; + QJsonValue operator[] (QLatin1String key) const { return value(key); } + QJsonValueRef operator[] (const QString &key); + QJsonValueRef operator[] (QLatin1String key); + + void remove(const QString &key); + QJsonValue take(const QString &key); + bool contains(const QString &key) const; + bool contains(QLatin1String key) const; + + bool operator==(const QJsonObject &other) const; + bool operator!=(const QJsonObject &other) const; + + class const_iterator; + + class iterator + { + friend class const_iterator; + friend class QJsonObject; + QJsonObject *o; + int i; + + public: + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef QJsonValue value_type; + typedef QJsonValueRef reference; + typedef QJsonValuePtr pointer; + + Q_DECL_CONSTEXPR inline iterator() : o(nullptr), i(0) {} + Q_DECL_CONSTEXPR inline iterator(QJsonObject *obj, int index) : o(obj), i(index) {} + + inline QString key() const { return o->keyAt(i); } + inline QJsonValueRef value() const { return QJsonValueRef(o, i); } + inline QJsonValueRef operator*() const { return QJsonValueRef(o, i); } +#ifdef Q_QDOC + inline QJsonValueRef* operator->() const; +#else + inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(o, i); } +#endif + inline bool operator==(const iterator &other) const { return i == other.i; } + inline bool operator!=(const iterator &other) const { return i != other.i; } + + inline iterator &operator++() { ++i; return *this; } + inline iterator operator++(int) { iterator r = *this; ++i; return r; } + inline iterator &operator--() { --i; return *this; } + inline iterator operator--(int) { iterator r = *this; --i; return r; } + inline iterator operator+(int j) const + { iterator r = *this; r.i += j; return r; } + inline iterator operator-(int j) const { return operator+(-j); } + inline iterator &operator+=(int j) { i += j; return *this; } + inline iterator &operator-=(int j) { i -= j; return *this; } + + public: + inline bool operator==(const const_iterator &other) const { return i == other.i; } + inline bool operator!=(const const_iterator &other) const { return i != other.i; } + }; + friend class iterator; + + class const_iterator + { + friend class iterator; + const QJsonObject *o; + int i; + + public: + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef QJsonValue value_type; + typedef QJsonValue reference; + typedef QJsonValuePtr pointer; + + Q_DECL_CONSTEXPR inline const_iterator() : o(nullptr), i(0) {} + Q_DECL_CONSTEXPR inline const_iterator(const QJsonObject *obj, int index) + : o(obj), i(index) {} + inline const_iterator(const iterator &other) + : o(other.o), i(other.i) {} + + inline QString key() const { return o->keyAt(i); } + inline QJsonValue value() const { return o->valueAt(i); } + inline QJsonValue operator*() const { return o->valueAt(i); } +#ifdef Q_QDOC + inline QJsonValue* operator->() const; +#else + inline QJsonValuePtr operator->() const { return QJsonValuePtr(o->valueAt(i)); } +#endif + inline bool operator==(const const_iterator &other) const { return i == other.i; } + inline bool operator!=(const const_iterator &other) const { return i != other.i; } + + inline const_iterator &operator++() { ++i; return *this; } + inline const_iterator operator++(int) { const_iterator r = *this; ++i; return r; } + inline const_iterator &operator--() { --i; return *this; } + inline const_iterator operator--(int) { const_iterator r = *this; --i; return r; } + inline const_iterator operator+(int j) const + { const_iterator r = *this; r.i += j; return r; } + inline const_iterator operator-(int j) const { return operator+(-j); } + inline const_iterator &operator+=(int j) { i += j; return *this; } + inline const_iterator &operator-=(int j) { i -= j; return *this; } + + inline bool operator==(const iterator &other) const { return i == other.i; } + inline bool operator!=(const iterator &other) const { return i != other.i; } + }; + friend class const_iterator; + + // STL style + inline iterator begin() { detach2(); return iterator(this, 0); } + inline const_iterator begin() const { return const_iterator(this, 0); } + inline const_iterator constBegin() const { return const_iterator(this, 0); } + inline iterator end() { detach2(); return iterator(this, size()); } + inline const_iterator end() const { return const_iterator(this, size()); } + inline const_iterator constEnd() const { return const_iterator(this, size()); } + iterator erase(iterator it); + + // more Qt + typedef iterator Iterator; + typedef const_iterator ConstIterator; + iterator find(const QString &key); + iterator find(QLatin1String key); + const_iterator find(const QString &key) const { return constFind(key); } + const_iterator find(QLatin1String key) const { return constFind(key); } + const_iterator constFind(const QString &key) const; + const_iterator constFind(QLatin1String key) const; + iterator insert(const QString &key, const QJsonValue &value); + + // STL compatibility + typedef QJsonValue mapped_type; + typedef QString key_type; + typedef int size_type; + + inline bool empty() const { return isEmpty(); } + +private: + friend class QJsonPrivate::Data; + friend class QJsonValue; + friend class QJsonDocument; + friend class QJsonValueRef; + + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); + + QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object); + void initialize(); + // ### Qt 6: remove me and merge with detach2 + void detach(uint reserve = 0); + bool detach2(uint reserve = 0); + void compact(); + + QString keyAt(int i) const; + QJsonValue valueAt(int i) const; + void setValueAt(int i, const QJsonValue &val); + + QJsonPrivate::Data *d; + QJsonPrivate::Object *o; +}; + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONOBJECT_H diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp new file mode 100644 index 0000000000..39738b90a8 --- /dev/null +++ b/src/corelib/serialization/qjsonparser.cpp @@ -0,0 +1,1027 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_BOOTSTRAPPED +#include <qcoreapplication.h> +#endif +#include <qdebug.h> +#include "qjsonparser_p.h" +#include "qjson_p.h" +#include "private/qutfcodec_p.h" + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() << "pos=" << current +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + +static const int nestingLimit = 1024; + +QT_BEGIN_NAMESPACE + +// error strings for the JSON parser +#define JSONERR_OK QT_TRANSLATE_NOOP("QJsonParseError", "no error occurred") +#define JSONERR_UNTERM_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "unterminated object") +#define JSONERR_MISS_NSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing name separator") +#define JSONERR_UNTERM_AR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated array") +#define JSONERR_MISS_VSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing value separator") +#define JSONERR_ILLEGAL_VAL QT_TRANSLATE_NOOP("QJsonParseError", "illegal value") +#define JSONERR_END_OF_NUM QT_TRANSLATE_NOOP("QJsonParseError", "invalid termination by number") +#define JSONERR_ILLEGAL_NUM QT_TRANSLATE_NOOP("QJsonParseError", "illegal number") +#define JSONERR_STR_ESC_SEQ QT_TRANSLATE_NOOP("QJsonParseError", "invalid escape sequence") +#define JSONERR_STR_UTF8 QT_TRANSLATE_NOOP("QJsonParseError", "invalid UTF8 string") +#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string") +#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma") +#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document") +#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document") +#define JSONERR_GARBAGEEND QT_TRANSLATE_NOOP("QJsonParseError", "garbage at the end of the document") + +/*! + \class QJsonParseError + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonParseError class is used to report errors during JSON parsing. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \enum QJsonParseError::ParseError + + This enum describes the type of error that occurred during the parsing of a JSON document. + + \value NoError No error occurred + \value UnterminatedObject An object is not correctly terminated with a closing curly bracket + \value MissingNameSeparator A comma separating different items is missing + \value UnterminatedArray The array is not correctly terminated with a closing square bracket + \value MissingValueSeparator A colon separating keys from values inside objects is missing + \value IllegalValue The value is illegal + \value TerminationByNumber The input stream ended while parsing a number + \value IllegalNumber The number is not well formed + \value IllegalEscapeSequence An illegal escape sequence occurred in the input + \value IllegalUTF8String An illegal UTF8 sequence occurred in the input + \value UnterminatedString A string wasn't terminated with a quote + \value MissingObject An object was expected but couldn't be found + \value DeepNesting The JSON document is too deeply nested for the parser to parse it + \value DocumentTooLarge The JSON document is too large for the parser to parse it + \value GarbageAtEnd The parsed document contains additional garbage characters at the end + +*/ + +/*! + \variable QJsonParseError::error + + Contains the type of the parse error. Is equal to QJsonParseError::NoError if the document + was parsed correctly. + + \sa ParseError, errorString() +*/ + + +/*! + \variable QJsonParseError::offset + + Contains the offset in the input string where the parse error occurred. + + \sa error, errorString() +*/ + +/*! + Returns the human-readable message appropriate to the reported JSON parsing error. + + \sa error + */ +QString QJsonParseError::errorString() const +{ + const char *sz = ""; + switch (error) { + case NoError: + sz = JSONERR_OK; + break; + case UnterminatedObject: + sz = JSONERR_UNTERM_OBJ; + break; + case MissingNameSeparator: + sz = JSONERR_MISS_NSEP; + break; + case UnterminatedArray: + sz = JSONERR_UNTERM_AR; + break; + case MissingValueSeparator: + sz = JSONERR_MISS_VSEP; + break; + case IllegalValue: + sz = JSONERR_ILLEGAL_VAL; + break; + case TerminationByNumber: + sz = JSONERR_END_OF_NUM; + break; + case IllegalNumber: + sz = JSONERR_ILLEGAL_NUM; + break; + case IllegalEscapeSequence: + sz = JSONERR_STR_ESC_SEQ; + break; + case IllegalUTF8String: + sz = JSONERR_STR_UTF8; + break; + case UnterminatedString: + sz = JSONERR_UTERM_STR; + break; + case MissingObject: + sz = JSONERR_MISS_OBJ; + break; + case DeepNesting: + sz = JSONERR_DEEP_NEST; + break; + case DocumentTooLarge: + sz = JSONERR_DOC_LARGE; + break; + case GarbageAtEnd: + sz = JSONERR_GARBAGEEND; + break; + } +#ifndef QT_BOOTSTRAPPED + return QCoreApplication::translate("QJsonParseError", sz); +#else + return QLatin1String(sz); +#endif +} + +using namespace QJsonPrivate; + +Parser::Parser(const char *json, int length) + : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(QJsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +void Parser::eatBOM() +{ + // eat UTF-8 byte order mark + uchar utf8bom[3] = { 0xef, 0xbb, 0xbf }; + if (end - json > 3 && + (uchar)json[0] == utf8bom[0] && + (uchar)json[1] == utf8bom[1] && + (uchar)json[2] == utf8bom[2]) + json += 3; +} + +bool Parser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +char Parser::nextToken() +{ + if (!eatSpace()) + return 0; + char token = *json++; + switch (token) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +QJsonDocument Parser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug(">>>>> parser begin"); +#endif + // allocate some space + dataLength = qMax(end - json, (ptrdiff_t) 256); + data = (char *)malloc(dataLength); + + // fill in Header data + QJsonPrivate::Header *h = (QJsonPrivate::Header *)data; + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1u; + + current = sizeof(QJsonPrivate::Header); + + eatBOM(); + char token = nextToken(); + + DEBUG << hex << (uint)token; + if (token == BeginArray) { + if (!parseArray()) + goto error; + } else if (token == BeginObject) { + if (!parseObject()) + goto error; + } else { + lastError = QJsonParseError::IllegalValue; + goto error; + } + + eatSpace(); + if (json < end) { + lastError = QJsonParseError::GarbageAtEnd; + goto error; + } + + END; + { + if (error) { + error->offset = 0; + error->error = QJsonParseError::NoError; + } + QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current); + return QJsonDocument(d); + } + +error: +#ifdef PARSER_DEBUG + qDebug(">>>>> parser error"); +#endif + if (error) { + error->offset = json - head; + error->error = lastError; + } + free(data); + return QJsonDocument(); +} + + +void Parser::ParsedObject::insert(uint offset) { + const QJsonPrivate::Entry *newEntry = reinterpret_cast<const QJsonPrivate::Entry *>(parser->data + objectPosition + offset); + int min = 0; + int n = offsets.size(); + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= *newEntry) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < offsets.size() && *entryAt(min) == *newEntry) { + offsets[min] = offset; + } else { + offsets.insert(min, offset); + } +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +bool Parser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return false; + } + + int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object)); + if (objectOffset < 0) + return false; + BEGIN << "parseObject pos=" << objectOffset << current << json; + + ParsedObject parsedObject(this, objectOffset); + + char token = nextToken(); + while (token == Quote) { + int off = current - objectOffset; + if (!parseMember(objectOffset)) + return false; + parsedObject.insert(off); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return false; + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return false; + } + + DEBUG << "numEntries" << parsedObject.offsets.size(); + int table = objectOffset; + // finalize the object + if (parsedObject.offsets.size()) { + int tableSize = parsedObject.offsets.size()*sizeof(uint); + table = reserveSpace(tableSize); + if (table < 0) + return false; + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + memcpy(data + table, parsedObject.offsets.constData(), tableSize); +#else + offset *o = (offset *)(data + table); + for (int i = 0; i < parsedObject.offsets.size(); ++i) + o[i] = parsedObject.offsets[i]; + +#endif + } + + QJsonPrivate::Object *o = (QJsonPrivate::Object *)(data + objectOffset); + o->tableOffset = table - objectOffset; + o->size = current - objectOffset; + o->is_object = true; + o->length = parsedObject.offsets.size(); + + DEBUG << "current=" << current; + END; + + --nestingLevel; + return true; +} + +/* + member = string name-separator value +*/ +bool Parser::parseMember(int baseOffset) +{ + int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry)); + if (entryOffset < 0) + return false; + BEGIN << "parseMember pos=" << entryOffset; + + bool latin1; + if (!parseString(&latin1)) + return false; + char token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; + } + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedObject; + return false; + } + QJsonPrivate::Value val; + if (!parseValue(&val, baseOffset)) + return false; + + // finalize the entry + QJsonPrivate::Entry *e = (QJsonPrivate::Entry *)(data + entryOffset); + e->value = val; + e->value.latinKey = latin1; + + END; + return true; +} + +namespace { + struct ValueArray { + static const int prealloc = 128; + ValueArray() : data(stackValues), alloc(prealloc), size(0) {} + ~ValueArray() { if (data != stackValues) free(data); } + + inline bool grow() { + alloc *= 2; + if (data == stackValues) { + QJsonPrivate::Value *newValues = static_cast<QJsonPrivate::Value *>(malloc(alloc*sizeof(QJsonPrivate::Value))); + if (!newValues) + return false; + memcpy(newValues, data, size*sizeof(QJsonPrivate::Value)); + data = newValues; + } else { + void *newValues = realloc(data, alloc * sizeof(QJsonPrivate::Value)); + if (!newValues) + return false; + data = static_cast<QJsonPrivate::Value *>(newValues); + } + return true; + } + bool append(const QJsonPrivate::Value &v) { + if (alloc == size && !grow()) + return false; + data[size] = v; + ++size; + return true; + } + + QJsonPrivate::Value stackValues[prealloc]; + QJsonPrivate::Value *data; + int alloc; + int size; + }; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +bool Parser::parseArray() +{ + BEGIN << "parseArray"; + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return false; + } + + int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array)); + if (arrayOffset < 0) + return false; + + ValueArray values; + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return false; + } + if (*json == EndArray) { + nextToken(); + } else { + while (1) { + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return false; + } + QJsonPrivate::Value val; + if (!parseValue(&val, arrayOffset)) + return false; + if (!values.append(val)) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + char token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return false; + } + } + } + + DEBUG << "size =" << values.size; + int table = arrayOffset; + // finalize the object + if (values.size) { + int tableSize = values.size*sizeof(QJsonPrivate::Value); + table = reserveSpace(tableSize); + if (table < 0) + return false; + memcpy(data + table, values.data, tableSize); + } + + QJsonPrivate::Array *a = (QJsonPrivate::Array *)(data + arrayOffset); + a->tableOffset = table - arrayOffset; + a->size = current - arrayOffset; + a->is_object = false; + a->length = values.size; + + DEBUG << "current=" << current; + END; + + --nestingLevel; + return true; +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) +{ + BEGIN << "parse Value" << json; + val->_dummy = 0; + + switch (*json++) { + case 'n': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + val->type = QJsonValue::Null; + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + val->type = QJsonValue::Bool; + val->value = true; + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 5) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + val->type = QJsonValue::Bool; + val->value = false; + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + val->type = QJsonValue::String; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + bool latin1; + if (!parseString(&latin1)) + return false; + val->latinOrIntValue = latin1; + DEBUG << "value: string"; + END; + return true; + } + case BeginArray: + val->type = QJsonValue::Array; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseArray()) + return false; + DEBUG << "value: array"; + END; + return true; + case BeginObject: + val->type = QJsonValue::Object; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseObject()) + return false; + DEBUG << "value: object"; + END; + return true; + case ValueSeparator: + // Essentially missing value, but after a colon, not after a comma + // like the other MissingObject errors. + lastError = QJsonParseError::IllegalValue; + return false; + case EndObject: + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val, baseOffset)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) +{ + BEGIN << "parseNumber" << json; + val->type = QJsonValue::Double; + + const char *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + if (json >= end) { + lastError = QJsonParseError::TerminationByNumber; + return false; + } + + QByteArray number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + val->int_value = n; + val->latinOrIntValue = true; + END; + return true; + } + } + + bool ok; + union { + quint64 ui; + double d; + }; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + int pos = reserveSpace(sizeof(double)); + if (pos < 0) + return false; + qToLittleEndian(ui, data + pos); + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = pos - baseOffset; + val->latinOrIntValue = false; + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(char digit, uint *result) +{ + *result <<= 4; + if (digit >= '0' && digit <= '9') + *result |= (digit - '0'); + else if (digit >= 'a' && digit <= 'f') + *result |= (digit - 'a') + 10; + else if (digit >= 'A' && digit <= 'F') + *result |= (digit - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const char *&json, const char *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape" << (char)*json; + uint escaped = *json++; + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + return true; + } + default: + // this is not as strict as one could be, but allows for more Json files + // to be parsed correctly. + *ch = escaped; + return true; + } + return true; +} + +static inline bool scanUtf8Char(const char *&json, const char *end, uint *result) +{ + const uchar *&src = reinterpret_cast<const uchar *&>(json); + const uchar *uend = reinterpret_cast<const uchar *>(end); + uchar b = *src++; + int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, result, src, uend); + if (res < 0) { + // decoding error, backtrack the character we read above + --json; + return false; + } + + return true; +} + +bool Parser::parseString(bool *latin1) +{ + *latin1 = true; + + const char *start = json; + int outStart = current; + + // try to write out a latin1 string + + int stringPos = reserveSpace(2); + if (stringPos < 0) + return false; + + BEGIN << "parse string stringPos=" << stringPos << json; + while (json < end) { + uint ch = 0; + if (*json == '"') + break; + else if (*json == '\\') { + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + } else { + if (!scanUtf8Char(json, end, &ch)) { + lastError = QJsonParseError::IllegalUTF8String; + return false; + } + } + // bail out if the string is not pure latin1 or too long to hold as a latin1string (which has only 16 bit for the length) + if (ch > 0xff || json - start >= 0x8000) { + *latin1 = false; + break; + } + int pos = reserveSpace(1); + if (pos < 0) + return false; + DEBUG << " " << ch << (char)ch; + data[pos] = (uchar)ch; + } + ++json; + DEBUG << "end of string"; + if (json >= end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + // no unicode string, we are done + if (*latin1) { + // write string length + *(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort)); + int pos = reserveSpace((4 - current) & 3); + if (pos < 0) + return false; + while (pos & 3) + data[pos++] = 0; + END; + return true; + } + + *latin1 = false; + DEBUG << "not latin"; + + json = start; + current = outStart + sizeof(int); + + while (json < end) { + uint ch = 0; + if (*json == '"') + break; + else if (*json == '\\') { + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + } else { + if (!scanUtf8Char(json, end, &ch)) { + lastError = QJsonParseError::IllegalUTF8String; + return false; + } + } + if (QChar::requiresSurrogates(ch)) { + int pos = reserveSpace(4); + if (pos < 0) + return false; + *(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch); + *(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch); + } else { + int pos = reserveSpace(2); + if (pos < 0) + return false; + *(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch; + } + } + ++json; + + if (json >= end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + // write string length + *(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2; + int pos = reserveSpace((4 - current) & 3); + if (pos < 0) + return false; + while (pos & 3) + data[pos++] = 0; + END; + return true; +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h new file mode 100644 index 0000000000..379256847f --- /dev/null +++ b/src/corelib/serialization/qjsonparser_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONPARSER_P_H +#define QJSONPARSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <qjsondocument.h> +#include <qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +namespace QJsonPrivate { + +class Parser +{ +public: + Parser(const char *json, int length); + + QJsonDocument parse(QJsonParseError *error); + + class ParsedObject + { + public: + ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) { + offsets.reserve(64); + } + void insert(uint offset); + + Parser *parser; + int objectPosition; + QVector<uint> offsets; + + inline QJsonPrivate::Entry *entryAt(int i) const { + return reinterpret_cast<QJsonPrivate::Entry *>(parser->data + objectPosition + offsets[i]); + } + }; + + +private: + inline void eatBOM(); + inline bool eatSpace(); + inline char nextToken(); + + bool parseObject(); + bool parseArray(); + bool parseMember(int baseOffset); + bool parseString(bool *latin1); + bool parseValue(QJsonPrivate::Value *val, int baseOffset); + bool parseNumber(QJsonPrivate::Value *val, int baseOffset); + const char *head; + const char *json; + const char *end; + + char *data; + int dataLength; + int current; + int nestingLevel; + QJsonParseError::ParseError lastError; + + inline int reserveSpace(int space) { + if (current + space >= dataLength) { + dataLength = 2*dataLength + space; + char *newData = (char *)realloc(data, dataLength); + if (!newData) { + lastError = QJsonParseError::DocumentTooLarge; + return -1; + } + data = newData; + } + int pos = current; + current += space; + return pos; + } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp new file mode 100644 index 0000000000..33707b6ec3 --- /dev/null +++ b/src/corelib/serialization/qjsonvalue.cpp @@ -0,0 +1,863 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsonarray.h> +#include <qvariant.h> +#include <qstringlist.h> +#include <qdebug.h> + +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJsonValue + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonValue class encapsulates a value in JSON. + + A value in JSON can be one of 6 basic types: + + JSON is a format to store structured data. It has 6 basic data types: + + \list + \li bool QJsonValue::Bool + \li double QJsonValue::Double + \li string QJsonValue::String + \li array QJsonValue::Array + \li object QJsonValue::Object + \li null QJsonValue::Null + \endlist + + A value can represent any of the above data types. In addition, QJsonValue has one special + flag to represent undefined values. This can be queried with isUndefined(). + + The type of the value can be queried with type() or accessors like isBool(), isString(), and so on. + Likewise, the value can be converted to the type stored in it using the toBool(), toString() and so on. + + Values are strictly typed internally and contrary to QVariant will not attempt to do any implicit type + conversions. This implies that converting to a type that is not stored in the value will return a default + constructed return value. + + \section1 QJsonValueRef + + QJsonValueRef is a helper class for QJsonArray and QJsonObject. + When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + The following methods return QJsonValueRef: + \list + \li \l {QJsonArray}::operator[](int i) + \li \l {QJsonObject}::operator[](const QString & key) const + \endlist + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + Creates a QJsonValue of type \a type. + + The default is to create a Null value. + */ +QJsonValue::QJsonValue(Type type) + : ui(0), d(0), t(type) +{ +} + +/*! + \internal + */ +QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v) + : d(0) +{ + t = (Type)(uint)v.type; + switch (t) { + case Undefined: + case Null: + dbl = 0; + break; + case Bool: + b = v.toBoolean(); + break; + case Double: + dbl = v.toDouble(base); + break; + case String: { + QString s = v.toString(base); + stringData = s.data_ptr(); + stringData->ref.ref(); + break; + } + case Array: + case Object: + d = data; + this->base = v.base(base); + break; + } + if (d) + d->ref.ref(); +} + +/*! + Creates a value of type Bool, with value \a b. + */ +QJsonValue::QJsonValue(bool b) + : d(0), t(Bool) +{ + this->b = b; +} + +/*! + Creates a value of type Double, with value \a n. + */ +QJsonValue::QJsonValue(double n) + : d(0), t(Double) +{ + this->dbl = n; +} + +/*! + \overload + Creates a value of type Double, with value \a n. + */ +QJsonValue::QJsonValue(int n) + : d(0), t(Double) +{ + this->dbl = n; +} + +/*! + \overload + Creates a value of type Double, with value \a n. + NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992). + If you pass in values outside this range expect a loss of precision to occur. + */ +QJsonValue::QJsonValue(qint64 n) + : d(0), t(Double) +{ + this->dbl = double(n); +} + +/*! + Creates a value of type String, with value \a s. + */ +QJsonValue::QJsonValue(const QString &s) + : d(0), t(String) +{ + stringDataFromQStringHelper(s); +} + +/*! + \fn QJsonValue::QJsonValue(const char *s) + + Creates a value of type String with value \a s, assuming + UTF-8 encoding of the input. + + You can disable this constructor by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. + + \since 5.3 + */ + +void QJsonValue::stringDataFromQStringHelper(const QString &string) +{ + stringData = *(QStringData **)(&string); + stringData->ref.ref(); +} + +/*! + Creates a value of type String, with value \a s. + */ +QJsonValue::QJsonValue(QLatin1String s) + : d(0), t(String) +{ + // ### FIXME: Avoid creating the temp QString below + QString str(s); + stringDataFromQStringHelper(str); +} + +/*! + Creates a value of type Array, with value \a a. + */ +QJsonValue::QJsonValue(const QJsonArray &a) + : d(a.d), t(Array) +{ + base = a.a; + if (d) + d->ref.ref(); +} + +/*! + Creates a value of type Object, with value \a o. + */ +QJsonValue::QJsonValue(const QJsonObject &o) + : d(o.d), t(Object) +{ + base = o.o; + if (d) + d->ref.ref(); +} + + +/*! + Destroys the value. + */ +QJsonValue::~QJsonValue() +{ + if (t == String && stringData && !stringData->ref.deref()) + free(stringData); + + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + */ +QJsonValue::QJsonValue(const QJsonValue &other) +{ + t = other.t; + d = other.d; + ui = other.ui; + if (d) + d->ref.ref(); + + if (t == String && stringData) + stringData->ref.ref(); +} + +/*! + Assigns the value stored in \a other to this object. + */ +QJsonValue &QJsonValue::operator =(const QJsonValue &other) +{ + QJsonValue copy(other); + swap(copy); + return *this; +} + +/*! + \fn QJsonValue::QJsonValue(QJsonValue &&other) + \since 5.10 + + Move-constructs a QJsonValue from \a other. +*/ + +/*! + \fn QJsonValue &QJsonValue::operator =(QJsonValue &&other) + \since 5.10 + + Move-assigns \a other to this value. +*/ + +/*! + \fn void QJsonValue::swap(QJsonValue &other) + \since 5.10 + + Swaps the value \a other with this. This operation is very fast and never fails. +*/ + +/*! + \fn bool QJsonValue::isNull() const + + Returns \c true if the value is null. +*/ + +/*! + \fn bool QJsonValue::isBool() const + + Returns \c true if the value contains a boolean. + + \sa toBool() + */ + +/*! + \fn bool QJsonValue::isDouble() const + + Returns \c true if the value contains a double. + + \sa toDouble() + */ + +/*! + \fn bool QJsonValue::isString() const + + Returns \c true if the value contains a string. + + \sa toString() + */ + +/*! + \fn bool QJsonValue::isArray() const + + Returns \c true if the value contains an array. + + \sa toArray() + */ + +/*! + \fn bool QJsonValue::isObject() const + + Returns \c true if the value contains an object. + + \sa toObject() + */ + +/*! + \fn bool QJsonValue::isUndefined() const + + Returns \c true if the value is undefined. This can happen in certain + error cases as e.g. accessing a non existing key in a QJsonObject. + */ + + +/*! + Converts \a variant to a QJsonValue and returns it. + + The conversion will convert QVariant types as follows: + + \table + \header + \li Source type + \li Destination type + \row + \li + \list + \li QMetaType::Nullptr + \endlist + \li QJsonValue::Null + \row + \li + \list + \li QMetaType::Bool + \endlist + \li QJsonValue::Bool + \row + \li + \list + \li QMetaType::Int + \li QMetaType::UInt + \li QMetaType::LongLong + \li QMetaType::ULongLong + \li QMetaType::Float + \li QMetaType::Double + \endlist + \li QJsonValue::Double + \row + \li + \list + \li QMetaType::QString + \endlist + \li QJsonValue::String + \row + \li + \list + \li QMetaType::QStringList + \li QMetaType::QVariantList + \endlist + \li QJsonValue::Array + \row + \li + \list + \li QMetaType::QVariantMap + \li QMetaType::QVariantHash + \endlist + \li QJsonValue::Object + \endtable + + For all other QVariant types a conversion to a QString will be attempted. If the returned string + is empty, a Null QJsonValue will be stored, otherwise a String value using the returned QString. + + \sa toVariant() + */ +QJsonValue QJsonValue::fromVariant(const QVariant &variant) +{ + switch (variant.userType()) { + case QMetaType::Nullptr: + return QJsonValue(Null); + case QVariant::Bool: + return QJsonValue(variant.toBool()); + case QVariant::Int: + case QMetaType::Float: + case QVariant::Double: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::UInt: + return QJsonValue(variant.toDouble()); + case QVariant::String: + return QJsonValue(variant.toString()); + case QVariant::StringList: + return QJsonValue(QJsonArray::fromStringList(variant.toStringList())); + case QVariant::List: + return QJsonValue(QJsonArray::fromVariantList(variant.toList())); + case QVariant::Map: + return QJsonValue(QJsonObject::fromVariantMap(variant.toMap())); + case QVariant::Hash: + return QJsonValue(QJsonObject::fromVariantHash(variant.toHash())); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QJsonValue: + return variant.toJsonValue(); + case QMetaType::QJsonObject: + return variant.toJsonObject(); + case QMetaType::QJsonArray: + return variant.toJsonArray(); + case QMetaType::QJsonDocument: { + QJsonDocument doc = variant.toJsonDocument(); + return doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object()); + } +#endif + default: + break; + } + QString string = variant.toString(); + if (string.isEmpty()) + return QJsonValue(); + return QJsonValue(string); +} + +/*! + Converts the value to a \l {QVariant::}{QVariant()}. + + The QJsonValue types will be converted as follows: + + \value Null QMetaType::Nullptr + \value Bool QMetaType::Bool + \value Double QMetaType::Double + \value String QString + \value Array QVariantList + \value Object QVariantMap + \value Undefined \l {QVariant::}{QVariant()} + + \sa fromVariant() + */ +QVariant QJsonValue::toVariant() const +{ + switch (t) { + case Bool: + return b; + case Double: + return dbl; + case String: + return toString(); + case Array: + return d ? + QJsonArray(d, static_cast<QJsonPrivate::Array *>(base)).toVariantList() : + QVariantList(); + case Object: + return d ? + QJsonObject(d, static_cast<QJsonPrivate::Object *>(base)).toVariantMap() : + QVariantMap(); + case Null: + return QVariant::fromValue(nullptr); + case Undefined: + break; + } + return QVariant(); +} + +/*! + \enum QJsonValue::Type + + This enum describes the type of the JSON value. + + \value Null A Null value + \value Bool A boolean value. Use toBool() to convert to a bool. + \value Double A double. Use toDouble() to convert to a double. + \value String A string. Use toString() to convert to a QString. + \value Array An array. Use toArray() to convert to a QJsonArray. + \value Object An object. Use toObject() to convert to a QJsonObject. + \value Undefined The value is undefined. This is usually returned as an + error condition, when trying to read an out of bounds value + in an array or a non existent key in an object. +*/ + +/*! + Returns the type of the value. + + \sa QJsonValue::Type + */ +QJsonValue::Type QJsonValue::type() const +{ + return t; +} + +/*! + Converts the value to a bool and returns it. + + If type() is not bool, the \a defaultValue will be returned. + */ +bool QJsonValue::toBool(bool defaultValue) const +{ + if (t != Bool) + return defaultValue; + return b; +} + +/*! + Converts the value to an int and returns it. + + If type() is not Double or the value is not a whole number, + the \a defaultValue will be returned. + */ +int QJsonValue::toInt(int defaultValue) const +{ + if (t == Double && int(dbl) == dbl) + return int(dbl); + return defaultValue; +} + +/*! + Converts the value to a double and returns it. + + If type() is not Double, the \a defaultValue will be returned. + */ +double QJsonValue::toDouble(double defaultValue) const +{ + if (t != Double) + return defaultValue; + return dbl; +} + +/*! + Converts the value to a QString and returns it. + + If type() is not String, the \a defaultValue will be returned. + */ +QString QJsonValue::toString(const QString &defaultValue) const +{ + if (t != String) + return defaultValue; + stringData->ref.ref(); // the constructor below doesn't add a ref. + QStringDataPtr holder = { stringData }; + return QString(holder); +} + +/*! + Converts the value to a QString and returns it. + + If type() is not String, a null QString will be returned. + + \sa QString::isNull() + */ +QString QJsonValue::toString() const +{ + if (t != String) + return QString(); + stringData->ref.ref(); // the constructor below doesn't add a ref. + QStringDataPtr holder = { stringData }; + return QString(holder); +} + +/*! + Converts the value to an array and returns it. + + If type() is not Array, the \a defaultValue will be returned. + */ +QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const +{ + if (!d || t != Array) + return defaultValue; + + return QJsonArray(d, static_cast<QJsonPrivate::Array *>(base)); +} + +/*! + \overload + + Converts the value to an array and returns it. + + If type() is not Array, a \l{QJsonArray::}{QJsonArray()} will be returned. + */ +QJsonArray QJsonValue::toArray() const +{ + return toArray(QJsonArray()); +} + +/*! + Converts the value to an object and returns it. + + If type() is not Object, the \a defaultValue will be returned. + */ +QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const +{ + if (!d || t != Object) + return defaultValue; + + return QJsonObject(d, static_cast<QJsonPrivate::Object *>(base)); +} + +/*! + \overload + + Converts the value to an object and returns it. + + If type() is not Object, the \l {QJsonObject::}{QJsonObject()} will be returned. +*/ +QJsonObject QJsonValue::toObject() const +{ + return toObject(QJsonObject()); +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + Equivalent to calling toObject().value(key). + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist, + or if isObject() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject + */ +const QJsonValue QJsonValue::operator[](const QString &key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return toObject().value(key); +} + +/*! + \overload + \since 5.10 +*/ +const QJsonValue QJsonValue::operator[](QLatin1String key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return toObject().value(key); +} + +/*! + Returns a QJsonValue representing the value for index \a i. + + Equivalent to calling toArray().at(i). + + The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds, + or if isArray() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray + */ +const QJsonValue QJsonValue::operator[](int i) const +{ + if (!isArray()) + return QJsonValue(QJsonValue::Undefined); + + return toArray().at(i); +} + +/*! + Returns \c true if the value is equal to \a other. + */ +bool QJsonValue::operator==(const QJsonValue &other) const +{ + if (t != other.t) + return false; + + switch (t) { + case Undefined: + case Null: + break; + case Bool: + return b == other.b; + case Double: + return dbl == other.dbl; + case String: + return toString() == other.toString(); + case Array: + if (base == other.base) + return true; + if (!base) + return !other.base->length; + if (!other.base) + return !base->length; + return QJsonArray(d, static_cast<QJsonPrivate::Array *>(base)) + == QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(other.base)); + case Object: + if (base == other.base) + return true; + if (!base) + return !other.base->length; + if (!other.base) + return !base->length; + return QJsonObject(d, static_cast<QJsonPrivate::Object *>(base)) + == QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(other.base)); + } + return true; +} + +/*! + Returns \c true if the value is not equal to \a other. + */ +bool QJsonValue::operator!=(const QJsonValue &other) const +{ + return !(*this == other); +} + +/*! + \internal + */ +void QJsonValue::detach() +{ + if (!d) + return; + + QJsonPrivate::Data *x = d->clone(base); + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + base = static_cast<QJsonPrivate::Object *>(d->header->root()); +} + + +/*! + \class QJsonValueRef + \inmodule QtCore + \reentrant + \brief The QJsonValueRef class is a helper class for QJsonValue. + + \internal + + \ingroup json + + When you get an object of type QJsonValueRef, if you can assign to it, + the assignment will apply to the character in the string from + which you got the reference. That is its whole purpose in life. + + You can use it exactly in the same way as a reference to a QJsonValue. + + The QJsonValueRef becomes invalid once modifications are made to the + string: if you want to keep the character, copy it into a QJsonValue. + + Most of the QJsonValue member functions also exist in QJsonValueRef. + However, they are not explicitly documented here. +*/ + + +QJsonValueRef &QJsonValueRef::operator =(const QJsonValue &val) +{ + if (is_object) + o->setValueAt(index, val); + else + a->replace(index, val); + + return *this; +} + +QJsonValueRef &QJsonValueRef::operator =(const QJsonValueRef &ref) +{ + if (is_object) + o->setValueAt(index, ref); + else + a->replace(index, ref); + + return *this; +} + +QVariant QJsonValueRef::toVariant() const +{ + return toValue().toVariant(); +} + +QJsonArray QJsonValueRef::toArray() const +{ + return toValue().toArray(); +} + +QJsonObject QJsonValueRef::toObject() const +{ + return toValue().toObject(); +} + +QJsonValue QJsonValueRef::toValue() const +{ + if (!is_object) + return a->at(index); + return o->valueAt(index); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonValue &o) +{ + QDebugStateSaver saver(dbg); + switch (o.t) { + case QJsonValue::Undefined: + dbg << "QJsonValue(undefined)"; + break; + case QJsonValue::Null: + dbg << "QJsonValue(null)"; + break; + case QJsonValue::Bool: + dbg.nospace() << "QJsonValue(bool, " << o.toBool() << ')'; + break; + case QJsonValue::Double: + dbg.nospace() << "QJsonValue(double, " << o.toDouble() << ')'; + break; + case QJsonValue::String: + dbg.nospace() << "QJsonValue(string, " << o.toString() << ')'; + break; + case QJsonValue::Array: + dbg.nospace() << "QJsonValue(array, "; + dbg << o.toArray(); + dbg << ')'; + break; + case QJsonValue::Object: + dbg.nospace() << "QJsonValue(object, "; + dbg << o.toObject(); + dbg << ')'; + break; + } + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonvalue.h b/src/corelib/serialization/qjsonvalue.h new file mode 100644 index 0000000000..96538ebbf9 --- /dev/null +++ b/src/corelib/serialization/qjsonvalue.h @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONVALUE_H +#define QJSONVALUE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class QDebug; +class QVariant; +class QJsonArray; +class QJsonObject; + +namespace QJsonPrivate { + class Data; + class Base; + class Object; + class Header; + class Array; + class Value; + class Entry; +} + +class Q_CORE_EXPORT QJsonValue +{ +public: + enum Type { + Null = 0x0, + Bool = 0x1, + Double = 0x2, + String = 0x3, + Array = 0x4, + Object = 0x5, + Undefined = 0x80 + }; + + QJsonValue(Type = Null); + QJsonValue(bool b); + QJsonValue(double n); + QJsonValue(int n); + QJsonValue(qint64 n); + QJsonValue(const QString &s); + QJsonValue(QLatin1String s); +#ifndef QT_NO_CAST_FROM_ASCII + inline QT_ASCII_CAST_WARN QJsonValue(const char *s) + : d(nullptr), t(String) { stringDataFromQStringHelper(QString::fromUtf8(s)); } +#endif + QJsonValue(const QJsonArray &a); + QJsonValue(const QJsonObject &o); + + ~QJsonValue(); + + QJsonValue(const QJsonValue &other); + QJsonValue &operator =(const QJsonValue &other); + + QJsonValue(QJsonValue &&other) Q_DECL_NOTHROW + : ui(other.ui), + d(other.d), + t(other.t) + { + other.ui = 0; + other.d = nullptr; + other.t = Null; + } + + QJsonValue &operator =(QJsonValue &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + void swap(QJsonValue &other) Q_DECL_NOTHROW + { + qSwap(ui, other.ui); + qSwap(d, other.d); + qSwap(t, other.t); + } + + static QJsonValue fromVariant(const QVariant &variant); + QVariant toVariant() const; + + Type type() const; + inline bool isNull() const { return type() == Null; } + inline bool isBool() const { return type() == Bool; } + inline bool isDouble() const { return type() == Double; } + inline bool isString() const { return type() == String; } + inline bool isArray() const { return type() == Array; } + inline bool isObject() const { return type() == Object; } + inline bool isUndefined() const { return type() == Undefined; } + + bool toBool(bool defaultValue = false) const; + int toInt(int defaultValue = 0) const; + double toDouble(double defaultValue = 0) const; + QString toString() const; + QString toString(const QString &defaultValue) const; + QJsonArray toArray() const; + QJsonArray toArray(const QJsonArray &defaultValue) const; + QJsonObject toObject() const; + QJsonObject toObject(const QJsonObject &defaultValue) const; + + const QJsonValue operator[](const QString &key) const; + const QJsonValue operator[](QLatin1String key) const; + const QJsonValue operator[](int i) const; + + bool operator==(const QJsonValue &other) const; + bool operator!=(const QJsonValue &other) const; + +private: + // avoid implicit conversions from char * to bool + inline QJsonValue(const void *) {} + friend class QJsonPrivate::Value; + friend class QJsonArray; + friend class QJsonObject; + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); + + QJsonValue(QJsonPrivate::Data *d, QJsonPrivate::Base *b, const QJsonPrivate::Value& v); + void stringDataFromQStringHelper(const QString &string); + + void detach(); + + union { + quint64 ui; + bool b; + double dbl; + QStringData *stringData; + QJsonPrivate::Base *base; + }; + QJsonPrivate::Data *d; // needed for Objects and Arrays + Type t; +}; + +class Q_CORE_EXPORT QJsonValueRef +{ +public: + QJsonValueRef(QJsonArray *array, int idx) + : a(array), is_object(false), index(idx) {} + QJsonValueRef(QJsonObject *object, int idx) + : o(object), is_object(true), index(idx) {} + + inline operator QJsonValue() const { return toValue(); } + QJsonValueRef &operator = (const QJsonValue &val); + QJsonValueRef &operator = (const QJsonValueRef &val); + + QVariant toVariant() const; + inline QJsonValue::Type type() const { return toValue().type(); } + inline bool isNull() const { return type() == QJsonValue::Null; } + inline bool isBool() const { return type() == QJsonValue::Bool; } + inline bool isDouble() const { return type() == QJsonValue::Double; } + inline bool isString() const { return type() == QJsonValue::String; } + inline bool isArray() const { return type() == QJsonValue::Array; } + inline bool isObject() const { return type() == QJsonValue::Object; } + inline bool isUndefined() const { return type() == QJsonValue::Undefined; } + + inline bool toBool() const { return toValue().toBool(); } + inline int toInt() const { return toValue().toInt(); } + inline double toDouble() const { return toValue().toDouble(); } + inline QString toString() const { return toValue().toString(); } + QJsonArray toArray() const; + QJsonObject toObject() const; + + // ### Qt 6: Add default values + inline bool toBool(bool defaultValue) const { return toValue().toBool(defaultValue); } + inline int toInt(int defaultValue) const { return toValue().toInt(defaultValue); } + inline double toDouble(double defaultValue) const { return toValue().toDouble(defaultValue); } + inline QString toString(const QString &defaultValue) const { return toValue().toString(defaultValue); } + + inline bool operator==(const QJsonValue &other) const { return toValue() == other; } + inline bool operator!=(const QJsonValue &other) const { return toValue() != other; } + +private: + QJsonValue toValue() const; + + union { + QJsonArray *a; + QJsonObject *o; + }; + uint is_object : 1; + uint index : 31; +}; + +#ifndef Q_QDOC +// ### Qt 6: Get rid of these fake pointer classes +class QJsonValuePtr +{ + QJsonValue value; +public: + explicit QJsonValuePtr(const QJsonValue& val) + : value(val) {} + + QJsonValue& operator*() { return value; } + QJsonValue* operator->() { return &value; } +}; + +class QJsonValueRefPtr +{ + QJsonValueRef valueRef; +public: + QJsonValueRefPtr(QJsonArray *array, int idx) + : valueRef(array, idx) {} + QJsonValueRefPtr(QJsonObject *object, int idx) + : valueRef(object, idx) {} + + QJsonValueRef& operator*() { return valueRef; } + QJsonValueRef* operator->() { return &valueRef; } +}; +#endif + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonValue) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONVALUE_H diff --git a/src/corelib/serialization/qjsonwriter.cpp b/src/corelib/serialization/qjsonwriter.cpp new file mode 100644 index 0000000000..12ce20ef09 --- /dev/null +++ b/src/corelib/serialization/qjsonwriter.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <cmath> +#include <qlocale.h> +#include "qjsonwriter_p.h" +#include "qjson_p.h" +#include "private/qutfcodec_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QJsonPrivate; + +static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact); +static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact); + +static inline uchar hexdig(uint u) +{ + return (u < 0xa ? '0' + u : 'a' + u - 0xa); +} + +static QByteArray escapedString(const QString &s) +{ + const uchar replacement = '?'; + QByteArray ba(s.length(), Qt::Uninitialized); + + uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData())); + const uchar *ba_end = cursor + ba.length(); + const ushort *src = reinterpret_cast<const ushort *>(s.constBegin()); + const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd()); + + while (src != end) { + if (cursor >= ba_end - 6) { + // ensure we have enough space + int pos = cursor - (const uchar *)ba.constData(); + ba.resize(ba.size()*2); + cursor = (uchar *)ba.data() + pos; + ba_end = (const uchar *)ba.constData() + ba.length(); + } + + uint u = *src++; + if (u < 0x80) { + if (u < 0x20 || u == 0x22 || u == 0x5c) { + *cursor++ = '\\'; + switch (u) { + case 0x22: + *cursor++ = '"'; + break; + case 0x5c: + *cursor++ = '\\'; + break; + case 0x8: + *cursor++ = 'b'; + break; + case 0xc: + *cursor++ = 'f'; + break; + case 0xa: + *cursor++ = 'n'; + break; + case 0xd: + *cursor++ = 'r'; + break; + case 0x9: + *cursor++ = 't'; + break; + default: + *cursor++ = 'u'; + *cursor++ = '0'; + *cursor++ = '0'; + *cursor++ = hexdig(u>>4); + *cursor++ = hexdig(u & 0xf); + } + } else { + *cursor++ = (uchar)u; + } + } else { + if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) < 0) + *cursor++ = replacement; + } + } + + ba.resize(cursor - (const uchar *)ba.constData()); + return ba; +} + +static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &v, QByteArray &json, int indent, bool compact) +{ + QJsonValue::Type type = (QJsonValue::Type)(uint)v.type; + switch (type) { + case QJsonValue::Bool: + json += v.toBoolean() ? "true" : "false"; + break; + case QJsonValue::Double: { + const double d = v.toDouble(b); + if (qIsFinite(d)) { // +2 to format to ensure the expected precision + const double abs = std::abs(d); + json += QByteArray::number(d, abs == static_cast<quint64>(abs) ? 'f' : 'g', QLocale::FloatingPointShortest); + } else { + json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) + } + break; + } + case QJsonValue::String: + json += '"'; + json += escapedString(v.toString(b)); + json += '"'; + break; + case QJsonValue::Array: + json += compact ? "[" : "[\n"; + arrayContentToJson(static_cast<QJsonPrivate::Array *>(v.base(b)), json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += ']'; + break; + case QJsonValue::Object: + json += compact ? "{" : "{\n"; + objectContentToJson(static_cast<QJsonPrivate::Object *>(v.base(b)), json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += '}'; + break; + case QJsonValue::Null: + default: + json += "null"; + } +} + +static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) +{ + if (!a || !a->length) + return; + + QByteArray indentString(4*indent, ' '); + + uint i = 0; + while (1) { + json += indentString; + valueToJson(a, a->at(i), json, indent, compact); + + if (++i == a->length) { + if (!compact) + json += '\n'; + break; + } + + json += compact ? "," : ",\n"; + } +} + + +static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) +{ + if (!o || !o->length) + return; + + QByteArray indentString(4*indent, ' '); + + uint i = 0; + while (1) { + QJsonPrivate::Entry *e = o->entryAt(i); + json += indentString; + json += '"'; + json += escapedString(e->key()); + json += compact ? "\":" : "\": "; + valueToJson(o, e->value, json, indent, compact); + + if (++i == o->length) { + if (!compact) + json += '\n'; + break; + } + + json += compact ? "," : ",\n"; + } +} + +void Writer::objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) +{ + json.reserve(json.size() + (o ? (int)o->size : 16)); + json += compact ? "{" : "{\n"; + objectContentToJson(o, json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += compact ? "}" : "}\n"; +} + +void Writer::arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) +{ + json.reserve(json.size() + (a ? (int)a->size : 16)); + json += compact ? "[" : "[\n"; + arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += compact ? "]" : "]\n"; +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonwriter_p.h b/src/corelib/serialization/qjsonwriter_p.h new file mode 100644 index 0000000000..76a8460449 --- /dev/null +++ b/src/corelib/serialization/qjsonwriter_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONWRITER_P_H +#define QJSONWRITER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <qjsonvalue.h> + +QT_BEGIN_NAMESPACE + +namespace QJsonPrivate +{ + +class Writer +{ +public: + static void objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact = false); + static void arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact = false); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/serialization/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp new file mode 100644 index 0000000000..ee3cb4efcb --- /dev/null +++ b/src/corelib/serialization/qtextstream.cpp @@ -0,0 +1,3192 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QTEXTSTREAM_DEBUG +static const int QTEXTSTREAM_BUFFERSIZE = 16384; + +/*! + \class QTextStream + \inmodule QtCore + + \brief The QTextStream class provides a convenient interface for + reading and writing text. + + \ingroup io + \ingroup string-processing + \reentrant + + QTextStream can operate on a QIODevice, a QByteArray or a + QString. Using QTextStream's streaming operators, you can + conveniently read and write words, lines and numbers. For + generating text, QTextStream supports formatting options for field + padding and alignment, and formatting of numbers. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 0 + + It's also common to use QTextStream to read console input and write + console output. QTextStream is locale aware, and will automatically decode + standard input using the correct codec. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 1 + + Besides using QTextStream's constructors, you can also set the + device or string QTextStream operates on by calling setDevice() or + setString(). You can seek to a position by calling seek(), and + atEnd() will return true when there is no data left to be read. If + you call flush(), QTextStream will empty all data from its write + buffer into the device and call flush() on the device. + + Internally, QTextStream uses a Unicode based buffer, and + QTextCodec is used by QTextStream to automatically support + different character sets. By default, QTextCodec::codecForLocale() + is used for reading and writing, but you can also set the codec by + calling setCodec(). Automatic Unicode detection is also + supported. When this feature is enabled (the default behavior), + QTextStream will detect the UTF-16 or the UTF-32 BOM (Byte Order Mark) and + switch to the appropriate UTF codec when reading. QTextStream + does not write a BOM by default, but you can enable this by calling + setGenerateByteOrderMark(true). When QTextStream operates on a QString + directly, the codec is disabled. + + There are three general ways to use QTextStream when reading text + files: + + \list + + \li Chunk by chunk, by calling readLine() or readAll(). + + \li Word by word. QTextStream supports streaming into \l {QString}s, + \l {QByteArray}s and char* buffers. Words are delimited by space, and + leading white space is automatically skipped. + + \li Character by character, by streaming into QChar or char types. + This method is often used for convenient input handling when + parsing files, independent of character encoding and end-of-line + semantics. To skip white space, call skipWhiteSpace(). + + \endlist + + Since the text stream uses a buffer, you should not read from + the stream using the implementation of a superclass. For instance, + if you have a QFile and read from it directly using + QFile::readLine() instead of using the stream, the text stream's + internal position will be out of sync with the file's position. + + By default, when reading numbers from a stream of text, + QTextStream will automatically detect the number's base + representation. For example, if the number starts with "0x", it is + assumed to be in hexadecimal form. If it starts with the digits + 1-9, it is assumed to be in decimal form, and so on. You can set + the integer base, thereby disabling the automatic detection, by + calling setIntegerBase(). Example: + + \snippet code/src_corelib_io_qtextstream.cpp 2 + + QTextStream supports many formatting options for generating text. + You can set the field width and pad character by calling + setFieldWidth() and setPadChar(). Use setFieldAlignment() to set + the alignment within each field. For real numbers, call + setRealNumberNotation() and setRealNumberPrecision() to set the + notation (SmartNotation, ScientificNotation, FixedNotation) and precision in + digits of the generated number. Some extra number formatting + options are also available through setNumberFlags(). + + \target QTextStream manipulators + + Like \c <iostream> in the standard C++ library, QTextStream also + defines several global manipulator functions: + + \table + \header \li Manipulator \li Description + \row \li \c bin \li Same as setIntegerBase(2). + \row \li \c oct \li Same as setIntegerBase(8). + \row \li \c dec \li Same as setIntegerBase(10). + \row \li \c hex \li Same as setIntegerBase(16). + \row \li \c showbase \li Same as setNumberFlags(numberFlags() | ShowBase). + \row \li \c forcesign \li Same as setNumberFlags(numberFlags() | ForceSign). + \row \li \c forcepoint \li Same as setNumberFlags(numberFlags() | ForcePoint). + \row \li \c noshowbase \li Same as setNumberFlags(numberFlags() & ~ShowBase). + \row \li \c noforcesign \li Same as setNumberFlags(numberFlags() & ~ForceSign). + \row \li \c noforcepoint \li Same as setNumberFlags(numberFlags() & ~ForcePoint). + \row \li \c uppercasebase \li Same as setNumberFlags(numberFlags() | UppercaseBase). + \row \li \c uppercasedigits \li Same as setNumberFlags(numberFlags() | UppercaseDigits). + \row \li \c lowercasebase \li Same as setNumberFlags(numberFlags() & ~UppercaseBase). + \row \li \c lowercasedigits \li Same as setNumberFlags(numberFlags() & ~UppercaseDigits). + \row \li \c fixed \li Same as setRealNumberNotation(FixedNotation). + \row \li \c scientific \li Same as setRealNumberNotation(ScientificNotation). + \row \li \c left \li Same as setFieldAlignment(AlignLeft). + \row \li \c right \li Same as setFieldAlignment(AlignRight). + \row \li \c center \li Same as setFieldAlignment(AlignCenter). + \row \li \c endl \li Same as operator<<('\\n') and flush(). + \row \li \c flush \li Same as flush(). + \row \li \c reset \li Same as reset(). + \row \li \c ws \li Same as skipWhiteSpace(). + \row \li \c bom \li Same as setGenerateByteOrderMark(true). + \endtable + + In addition, Qt provides three global manipulators that take a + parameter: qSetFieldWidth(), qSetPadChar(), and + qSetRealNumberPrecision(). + + \sa QDataStream, QIODevice, QFile, QBuffer, QTcpSocket, {Text Codecs Example} +*/ + +/*! \enum QTextStream::RealNumberNotation + + This enum specifies which notations to use for expressing \c + float and \c double as strings. + + \value ScientificNotation Scientific notation (\c{printf()}'s \c %e flag). + \value FixedNotation Fixed-point notation (\c{printf()}'s \c %f flag). + \value SmartNotation Scientific or fixed-point notation, depending on which makes most sense (\c{printf()}'s \c %g flag). + + \sa setRealNumberNotation() +*/ + +/*! \enum QTextStream::FieldAlignment + + This enum specifies how to align text in fields when the field is + wider than the text that occupies it. + + \value AlignLeft Pad on the right side of fields. + \value AlignRight Pad on the left side of fields. + \value AlignCenter Pad on both sides of field. + \value AlignAccountingStyle Same as AlignRight, except that the + sign of a number is flush left. + + \sa setFieldAlignment() +*/ + +/*! \enum QTextStream::NumberFlag + + This enum specifies various flags that can be set to affect the + output of integers, \c{float}s, and \c{double}s. + + \value ShowBase Show the base as a prefix if the base + is 16 ("0x"), 8 ("0"), or 2 ("0b"). + \value ForcePoint Always put the decimal separator in numbers, even if + there are no decimals. + \value ForceSign Always put the sign in numbers, even for positive numbers. + \value UppercaseBase Use uppercase versions of base prefixes ("0X", "0B"). + \value UppercaseDigits Use uppercase letters for expressing + digits 10 to 35 instead of lowercase. + + \sa setNumberFlags() +*/ + +/*! \enum QTextStream::Status + + This enum describes the current status of the text stream. + + \value Ok The text stream is operating normally. + \value ReadPastEnd The text stream has read past the end of the + data in the underlying device. + \value ReadCorruptData The text stream has read corrupt data. + \value WriteFailed The text stream cannot write to the underlying device. + + \sa status() +*/ + +#include "qtextstream.h" +#include "private/qtextstream_p.h" +#include "qbuffer.h" +#include "qfile.h" +#include "qnumeric.h" +#include "qvarlengtharray.h" + +#include <locale.h> +#include "private/qlocale_p.h" + +#include <stdlib.h> +#include <limits.h> +#include <new> + +#if defined QTEXTSTREAM_DEBUG +#include <ctype.h> +#include "private/qtools_p.h" + +QT_BEGIN_NAMESPACE + +// Returns a human readable representation of the first \a len +// characters in \a data. +static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) +{ + if (!data) return "(null)"; + QByteArray out; + for (int i = 0; i < len; ++i) { + char c = data[i]; + if (isprint(int(uchar(c)))) { + out += c; + } else switch (c) { + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: { + const char buf[] = { + '\\', + 'x', + QtMiscUtils::toHexLower(uchar(c) / 16), + QtMiscUtils::toHexLower(uchar(c) % 16), + 0 + }; + out += buf; + } + } + } + + if (len < maxSize) + out += "..."; + + return out; +} +QT_END_NAMESPACE + +#endif + +// A precondition macro +#define Q_VOID +#define CHECK_VALID_STREAM(x) do { \ + if (!d->string && !d->device) { \ + qWarning("QTextStream: No device"); \ + return x; \ + } } while (0) + +// Base implementations of operator>> for ints and reals +#define IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(type) do { \ + Q_D(QTextStream); \ + CHECK_VALID_STREAM(*this); \ + qulonglong tmp; \ + switch (d->getNumber(&tmp)) { \ + case QTextStreamPrivate::npsOk: \ + i = (type)tmp; \ + break; \ + case QTextStreamPrivate::npsMissingDigit: \ + case QTextStreamPrivate::npsInvalidPrefix: \ + i = (type)0; \ + setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \ + break; \ + } \ + return *this; } while (0) + +#define IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(type) do { \ + Q_D(QTextStream); \ + CHECK_VALID_STREAM(*this); \ + double tmp; \ + if (d->getReal(&tmp)) { \ + f = (type)tmp; \ + } else { \ + f = (type)0; \ + setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \ + } \ + return *this; } while (0) + +QT_BEGIN_NAMESPACE + +//------------------------------------------------------------------- + +/*! + \internal +*/ +QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr) + : +#ifndef QT_NO_TEXTCODEC + readConverterSavedState(0), +#endif + readConverterSavedStateOffset(0), + locale(QLocale::c()) +{ + this->q_ptr = q_ptr; + reset(); +} + +/*! + \internal +*/ +QTextStreamPrivate::~QTextStreamPrivate() +{ + if (deleteDevice) { +#ifndef QT_NO_QOBJECT + device->blockSignals(true); +#endif + delete device; + } +#ifndef QT_NO_TEXTCODEC + delete readConverterSavedState; +#endif +} + +#ifndef QT_NO_TEXTCODEC +static void resetCodecConverterStateHelper(QTextCodec::ConverterState *state) +{ + state->~ConverterState(); + new (state) QTextCodec::ConverterState; +} + +static void copyConverterStateHelper(QTextCodec::ConverterState *dest, + const QTextCodec::ConverterState *src) +{ + // ### QTextCodec::ConverterState's copy constructors and assignments are + // private. This function copies the structure manually. + Q_ASSERT(!src->d); + dest->flags = src->flags; + dest->invalidChars = src->invalidChars; + dest->state_data[0] = src->state_data[0]; + dest->state_data[1] = src->state_data[1]; + dest->state_data[2] = src->state_data[2]; +} +#endif + +void QTextStreamPrivate::Params::reset() +{ + realNumberPrecision = 6; + integerBase = 0; + fieldWidth = 0; + padChar = QLatin1Char(' '); + fieldAlignment = QTextStream::AlignRight; + realNumberNotation = QTextStream::SmartNotation; + numberFlags = 0; +} + +/*! + \internal +*/ +void QTextStreamPrivate::reset() +{ + params.reset(); + + device = 0; + deleteDevice = false; + string = 0; + stringOffset = 0; + stringOpenMode = QIODevice::NotOpen; + + readBufferOffset = 0; + readBufferStartDevicePos = 0; + lastTokenSize = 0; + +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForLocale(); + resetCodecConverterStateHelper(&readConverterState); + resetCodecConverterStateHelper(&writeConverterState); + delete readConverterSavedState; + readConverterSavedState = 0; + writeConverterState.flags |= QTextCodec::IgnoreHeader; + autoDetectUnicode = true; +#endif +} + +/*! + \internal +*/ +bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) +{ + // no buffer next to the QString itself; this function should only + // be called internally, for devices. + Q_ASSERT(!string); + Q_ASSERT(device); + + // handle text translation and bypass the Text flag in the device. + bool textModeEnabled = device->isTextModeEnabled(); + if (textModeEnabled) + device->setTextModeEnabled(false); + + // read raw data into a temporary buffer + char buf[QTEXTSTREAM_BUFFERSIZE]; + qint64 bytesRead = 0; +#if defined(Q_OS_WIN) + // On Windows, there is no non-blocking stdin - so we fall back to reading + // lines instead. If there is no QOBJECT, we read lines for all sequential + // devices; otherwise, we read lines only for stdin. + QFile *file = 0; + Q_UNUSED(file); + if (device->isSequential() +#if !defined(QT_NO_QOBJECT) + && (file = qobject_cast<QFile *>(device)) && file->handle() == 0 +#endif + ) { + if (maxBytes != -1) + bytesRead = device->readLine(buf, qMin<qint64>(sizeof(buf), maxBytes)); + else + bytesRead = device->readLine(buf, sizeof(buf)); + } else +#endif + { + if (maxBytes != -1) + bytesRead = device->read(buf, qMin<qint64>(sizeof(buf), maxBytes)); + else + bytesRead = device->read(buf, sizeof(buf)); + } + + // reset the Text flag. + if (textModeEnabled) + device->setTextModeEnabled(true); + + if (bytesRead <= 0) + return false; + +#ifndef QT_NO_TEXTCODEC + // codec auto detection, explicitly defaults to locale encoding if the + // codec has been set to 0. + if (!codec || autoDetectUnicode) { + autoDetectUnicode = false; + + codec = QTextCodec::codecForUtfText(QByteArray::fromRawData(buf, bytesRead), codec); + if (!codec) { + codec = QTextCodec::codecForLocale(); + writeConverterState.flags |= QTextCodec::IgnoreHeader; + } + } +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::fillReadBuffer(), using %s codec", + codec ? codec->name().constData() : "no"); +#endif +#endif + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::fillReadBuffer(), device->read(\"%s\", %d) == %d", + qt_prettyDebug(buf, qMin(32,int(bytesRead)) , int(bytesRead)).constData(), int(sizeof(buf)), int(bytesRead)); +#endif + + int oldReadBufferSize = readBuffer.size(); +#ifndef QT_NO_TEXTCODEC + // convert to unicode + readBuffer += Q_LIKELY(codec) ? codec->toUnicode(buf, bytesRead, &readConverterState) + : QString::fromLatin1(buf, bytesRead); +#else + readBuffer += QString::fromLatin1(buf, bytesRead); +#endif + + // remove all '\r\n' in the string. + if (readBuffer.size() > oldReadBufferSize && textModeEnabled) { + QChar CR = QLatin1Char('\r'); + QChar *writePtr = readBuffer.data() + oldReadBufferSize; + QChar *readPtr = readBuffer.data() + oldReadBufferSize; + QChar *endPtr = readBuffer.data() + readBuffer.size(); + + int n = oldReadBufferSize; + if (readPtr < endPtr) { + // Cut-off to avoid unnecessary self-copying. + while (*readPtr++ != CR) { + ++n; + if (++writePtr == endPtr) + break; + } + } + while (readPtr < endPtr) { + QChar ch = *readPtr++; + if (ch != CR) { + *writePtr++ = ch; + } else { + if (n < readBufferOffset) + --readBufferOffset; + --bytesRead; + } + ++n; + } + readBuffer.resize(writePtr - readBuffer.data()); + } + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::fillReadBuffer() read %d bytes from device. readBuffer = [%s]", int(bytesRead), + qt_prettyDebug(readBuffer.toLatin1(), readBuffer.size(), readBuffer.size()).data()); +#endif + return true; +} + +/*! + \internal +*/ +void QTextStreamPrivate::resetReadBuffer() +{ + readBuffer.clear(); + readBufferOffset = 0; + readBufferStartDevicePos = (device ? device->pos() : 0); +} + +/*! + \internal +*/ +void QTextStreamPrivate::flushWriteBuffer() +{ + // no buffer next to the QString itself; this function should only + // be called internally, for devices. + if (string || !device) + return; + + // Stream went bye-bye already. Appending further data may succeed again, + // but would create a corrupted stream anyway. + if (status != QTextStream::Ok) + return; + + if (writeBuffer.isEmpty()) + return; + +#if defined (Q_OS_WIN) + // handle text translation and bypass the Text flag in the device. + bool textModeEnabled = device->isTextModeEnabled(); + if (textModeEnabled) { + device->setTextModeEnabled(false); + writeBuffer.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + } +#endif + +#ifndef QT_NO_TEXTCODEC + if (!codec) + codec = QTextCodec::codecForLocale(); +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::flushWriteBuffer(), using %s codec (%s generating BOM)", + codec ? codec->name().constData() : "no", + !codec || (writeConverterState.flags & QTextCodec::IgnoreHeader) ? "not" : ""); +#endif + + // convert from unicode to raw data + // codec might be null if we're already inside global destructors (QTestCodec::codecForLocale returned null) + QByteArray data = Q_LIKELY(codec) ? codec->fromUnicode(writeBuffer.data(), writeBuffer.size(), &writeConverterState) + : writeBuffer.toLatin1(); +#else + QByteArray data = writeBuffer.toLatin1(); +#endif + writeBuffer.clear(); + + // write raw data to the device + qint64 bytesWritten = device->write(data); +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d", + qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten)); +#endif + +#if defined (Q_OS_WIN) + // reset the text flag + if (textModeEnabled) + device->setTextModeEnabled(true); +#endif + + if (bytesWritten <= 0) { + status = QTextStream::WriteFailed; + return; + } + + // flush the file +#ifndef QT_NO_QOBJECT + QFileDevice *file = qobject_cast<QFileDevice *>(device); + bool flushed = !file || file->flush(); +#else + bool flushed = true; +#endif + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::flushWriteBuffer() wrote %d bytes", + int(bytesWritten)); +#endif + if (!flushed || bytesWritten != qint64(data.size())) + status = QTextStream::WriteFailed; +} + +QString QTextStreamPrivate::read(int maxlen) +{ + QString ret; + if (string) { + lastTokenSize = qMin(maxlen, string->size() - stringOffset); + ret = string->mid(stringOffset, lastTokenSize); + } else { + while (readBuffer.size() - readBufferOffset < maxlen && fillReadBuffer()) ; + lastTokenSize = qMin(maxlen, readBuffer.size() - readBufferOffset); + ret = readBuffer.mid(readBufferOffset, lastTokenSize); + } + consumeLastToken(); + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::read() maxlen = %d, token length = %d", maxlen, ret.length()); +#endif + return ret; +} + +/*! + \internal + + Scans no more than \a maxlen QChars in the current buffer for the + first \a delimiter. Stores a pointer to the start offset of the + token in \a ptr, and the length in QChars in \a length. +*/ +bool QTextStreamPrivate::scan(const QChar **ptr, int *length, int maxlen, TokenDelimiter delimiter) +{ + int totalSize = 0; + int delimSize = 0; + bool consumeDelimiter = false; + bool foundToken = false; + int startOffset = device ? readBufferOffset : stringOffset; + QChar lastChar; + + bool canStillReadFromDevice = true; + do { + int endOffset; + const QChar *chPtr; + if (device) { + chPtr = readBuffer.constData(); + endOffset = readBuffer.size(); + } else { + chPtr = string->constData(); + endOffset = string->size(); + } + chPtr += startOffset; + + for (; !foundToken && startOffset < endOffset && (!maxlen || totalSize < maxlen); ++startOffset) { + const QChar ch = *chPtr++; + ++totalSize; + + switch (delimiter) { + case Space: + if (ch.isSpace()) { + foundToken = true; + delimSize = 1; + } + break; + case NotSpace: + if (!ch.isSpace()) { + foundToken = true; + delimSize = 1; + } + break; + case EndOfLine: + if (ch == QLatin1Char('\n')) { + foundToken = true; + delimSize = (lastChar == QLatin1Char('\r')) ? 2 : 1; + consumeDelimiter = true; + } + lastChar = ch; + break; + } + } + } while (!foundToken + && (!maxlen || totalSize < maxlen) + && (device && (canStillReadFromDevice = fillReadBuffer()))); + + if (totalSize == 0) { +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::scan() reached the end of input."); +#endif + return false; + } + + // if we find a '\r' at the end of the data when reading lines, + // don't make it part of the line. + if (delimiter == EndOfLine && totalSize > 0 && !foundToken) { + if (((string && stringOffset + totalSize == string->size()) || (device && device->atEnd())) + && lastChar == QLatin1Char('\r')) { + consumeDelimiter = true; + ++delimSize; + } + } + + // set the read offset and length of the token + if (length) + *length = totalSize - delimSize; + if (ptr) + *ptr = readPtr(); + + // update last token size. the callee will call consumeLastToken() when + // done. + lastTokenSize = totalSize; + if (!consumeDelimiter) + lastTokenSize -= delimSize; + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::scan(%p, %p, %d, %x) token length = %d, delimiter = %d", + ptr, length, maxlen, (int)delimiter, totalSize - delimSize, delimSize); +#endif + return true; +} + +/*! + \internal +*/ +inline const QChar *QTextStreamPrivate::readPtr() const +{ + Q_ASSERT(readBufferOffset <= readBuffer.size()); + if (string) + return string->constData() + stringOffset; + return readBuffer.constData() + readBufferOffset; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::consumeLastToken() +{ + if (lastTokenSize) + consume(lastTokenSize); + lastTokenSize = 0; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::consume(int size) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::consume(%d)", size); +#endif + if (string) { + stringOffset += size; + if (stringOffset > string->size()) + stringOffset = string->size(); + } else { + readBufferOffset += size; + if (readBufferOffset >= readBuffer.size()) { + readBufferOffset = 0; + readBuffer.clear(); + saveConverterState(device->pos()); + } else if (readBufferOffset > QTEXTSTREAM_BUFFERSIZE) { + readBuffer = readBuffer.remove(0,readBufferOffset); + readConverterSavedStateOffset += readBufferOffset; + readBufferOffset = 0; + } + } +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::saveConverterState(qint64 newPos) +{ +#ifndef QT_NO_TEXTCODEC + if (readConverterState.d) { + // converter cannot be copied, so don't save anything + // don't update readBufferStartDevicePos either + return; + } + + if (!readConverterSavedState) + readConverterSavedState = new QTextCodec::ConverterState; + copyConverterStateHelper(readConverterSavedState, &readConverterState); +#endif + + readBufferStartDevicePos = newPos; + readConverterSavedStateOffset = 0; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::restoreToSavedConverterState() +{ +#ifndef QT_NO_TEXTCODEC + if (readConverterSavedState) { + // we have a saved state + // that means the converter can be copied + copyConverterStateHelper(&readConverterState, readConverterSavedState); + } else { + // the only state we could save was the initial + // so reset to that + resetCodecConverterStateHelper(&readConverterState); + } +#endif +} + +/*! + \internal +*/ +void QTextStreamPrivate::write(const QChar *data, int len) +{ + if (string) { + // ### What about seek()?? + string->append(data, len); + } else { + writeBuffer.append(data, len); + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::write(QChar ch) +{ + if (string) { + // ### What about seek()?? + string->append(ch); + } else { + writeBuffer += ch; + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +void QTextStreamPrivate::write(QLatin1String data) +{ + if (string) { + // ### What about seek()?? + string->append(data); + } else { + writeBuffer += data; + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +void QTextStreamPrivate::writePadding(int len) +{ + if (string) { + // ### What about seek()?? + string->resize(string->size() + len, params.padChar); + } else { + writeBuffer.resize(writeBuffer.size() + len, params.padChar); + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +inline bool QTextStreamPrivate::getChar(QChar *ch) +{ + if ((string && stringOffset == string->size()) + || (device && readBuffer.isEmpty() && !fillReadBuffer())) { + if (ch) + *ch = 0; + return false; + } + if (ch) + *ch = *readPtr(); + consume(1); + return true; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::ungetChar(QChar ch) +{ + if (string) { + if (stringOffset == 0) + string->prepend(ch); + else + (*string)[--stringOffset] = ch; + return; + } + + if (readBufferOffset == 0) { + readBuffer.prepend(ch); + return; + } + + readBuffer[--readBufferOffset] = ch; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::putChar(QChar ch) +{ + if (params.fieldWidth > 0) + putString(&ch, 1); + else + write(ch); +} + + +/*! + \internal +*/ +QTextStreamPrivate::PaddingResult QTextStreamPrivate::padding(int len) const +{ + Q_ASSERT(params.fieldWidth > len); // calling padding() when no padding is needed is an error + + int left = 0, right = 0; + + const int padSize = params.fieldWidth - len; + + switch (params.fieldAlignment) { + case QTextStream::AlignLeft: + right = padSize; + break; + case QTextStream::AlignRight: + case QTextStream::AlignAccountingStyle: + left = padSize; + break; + case QTextStream::AlignCenter: + left = padSize/2; + right = padSize - padSize/2; + break; + } + return { left, right }; +} + +/*! + \internal +*/ +void QTextStreamPrivate::putString(const QChar *data, int len, bool number) +{ + if (Q_UNLIKELY(params.fieldWidth > len)) { + + // handle padding: + + const PaddingResult pad = padding(len); + + if (params.fieldAlignment == QTextStream::AlignAccountingStyle && number) { + const QChar sign = len > 0 ? data[0] : QChar(); + if (sign == locale.negativeSign() || sign == locale.positiveSign()) { + // write the sign before the padding, then skip it later + write(&sign, 1); + ++data; + --len; + } + } + + writePadding(pad.left); + write(data, len); + writePadding(pad.right); + } else { + write(data, len); + } +} + +/*! + \internal +*/ +void QTextStreamPrivate::putString(QLatin1String data, bool number) +{ + if (Q_UNLIKELY(params.fieldWidth > data.size())) { + + // handle padding + + const PaddingResult pad = padding(data.size()); + + if (params.fieldAlignment == QTextStream::AlignAccountingStyle && number) { + const QChar sign = data.size() > 0 ? QLatin1Char(*data.data()) : QChar(); + if (sign == locale.negativeSign() || sign == locale.positiveSign()) { + // write the sign before the padding, then skip it later + write(&sign, 1); + data = QLatin1String(data.data() + 1, data.size() - 1); + } + } + + writePadding(pad.left); + write(data); + writePadding(pad.right); + } else { + write(data); + } +} + +/*! + Constructs a QTextStream. Before you can use it for reading or + writing, you must assign a device or a string. + + \sa setDevice(), setString() +*/ +QTextStream::QTextStream() + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream()"); +#endif + Q_D(QTextStream); + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a device. +*/ +QTextStream::QTextStream(QIODevice *device) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(QIODevice *device == *%p)", + device); +#endif + Q_D(QTextStream); + d->device = device; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a string, using \a + openMode to define the open mode. +*/ +QTextStream::QTextStream(QString *string, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(QString *string == *%p, openMode = %d)", + string, int(openMode)); +#endif + Q_D(QTextStream); + d->string = string; + d->stringOpenMode = openMode; + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a array, using \a + openMode to define the open mode. Internally, the array is wrapped + by a QBuffer. +*/ +QTextStream::QTextStream(QByteArray *array, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(QByteArray *array == *%p, openMode = %d)", + array, int(openMode)); +#endif + Q_D(QTextStream); + d->device = new QBuffer(array); + d->device->open(openMode); + d->deleteDevice = true; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a array, using \a + openMode to define the open mode. The array is accessed as + read-only, regardless of the values in \a openMode. + + This constructor is convenient for working on constant + strings. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 3 +*/ +QTextStream::QTextStream(const QByteArray &array, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(const QByteArray &array == *(%p), openMode = %d)", + &array, int(openMode)); +#endif + QBuffer *buffer = new QBuffer; + buffer->setData(array); + buffer->open(openMode); + + Q_D(QTextStream); + d->device = buffer; + d->deleteDevice = true; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a fileHandle, using \a + openMode to define the open mode. Internally, a QFile is created + to handle the FILE pointer. + + This constructor is useful for working directly with the common + FILE based input and output streams: stdin, stdout and stderr. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 4 +*/ + +QTextStream::QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(FILE *fileHandle = %p, openMode = %d)", + fileHandle, int(openMode)); +#endif + QFile *file = new QFile; + file->open(fileHandle, openMode); + + Q_D(QTextStream); + d->device = file; + d->deleteDevice = true; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Destroys the QTextStream. + + If the stream operates on a device, flush() will be called + implicitly. Otherwise, the device is unaffected. +*/ +QTextStream::~QTextStream() +{ + Q_D(QTextStream); +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::~QTextStream()"); +#endif + if (!d->writeBuffer.isEmpty()) + d->flushWriteBuffer(); +} + +/*! + Resets QTextStream's formatting options, bringing it back to its + original constructed state. The device, string and any buffered + data is left untouched. +*/ +void QTextStream::reset() +{ + Q_D(QTextStream); + + d->params.reset(); +} + +/*! + Flushes any buffered data waiting to be written to the device. + + If QTextStream operates on a string, this function does nothing. +*/ +void QTextStream::flush() +{ + Q_D(QTextStream); + d->flushWriteBuffer(); +} + +/*! + Seeks to the position \a pos in the device. Returns \c true on + success; otherwise returns \c false. +*/ +bool QTextStream::seek(qint64 pos) +{ + Q_D(QTextStream); + d->lastTokenSize = 0; + + if (d->device) { + // Empty the write buffer + d->flushWriteBuffer(); + if (!d->device->seek(pos)) + return false; + d->resetReadBuffer(); + +#ifndef QT_NO_TEXTCODEC + // Reset the codec converter states. + resetCodecConverterStateHelper(&d->readConverterState); + resetCodecConverterStateHelper(&d->writeConverterState); + delete d->readConverterSavedState; + d->readConverterSavedState = 0; + d->writeConverterState.flags |= QTextCodec::IgnoreHeader; +#endif + return true; + } + + // string + if (d->string && pos <= d->string->size()) { + d->stringOffset = int(pos); + return true; + } + return false; +} + +/*! + \since 4.2 + + Returns the device position corresponding to the current position of the + stream, or -1 if an error occurs (e.g., if there is no device or string, + or if there's a device error). + + Because QTextStream is buffered, this function may have to + seek the device to reconstruct a valid device position. This + operation can be expensive, so you may want to avoid calling this + function in a tight loop. + + \sa seek() +*/ +qint64 QTextStream::pos() const +{ + Q_D(const QTextStream); + if (d->device) { + // Cutoff + if (d->readBuffer.isEmpty()) + return d->device->pos(); + if (d->device->isSequential()) + return 0; + + // Seek the device + if (!d->device->seek(d->readBufferStartDevicePos)) + return qint64(-1); + + // Reset the read buffer + QTextStreamPrivate *thatd = const_cast<QTextStreamPrivate *>(d); + thatd->readBuffer.clear(); + +#ifndef QT_NO_TEXTCODEC + thatd->restoreToSavedConverterState(); + if (d->readBufferStartDevicePos == 0) + thatd->autoDetectUnicode = true; +#endif + + // Rewind the device to get to the current position Ensure that + // readBufferOffset is unaffected by fillReadBuffer() + int oldReadBufferOffset = d->readBufferOffset + d->readConverterSavedStateOffset; + while (d->readBuffer.size() < oldReadBufferOffset) { + if (!thatd->fillReadBuffer(1)) + return qint64(-1); + } + thatd->readBufferOffset = oldReadBufferOffset; + thatd->readConverterSavedStateOffset = 0; + + // Return the device position. + return d->device->pos(); + } + + if (d->string) + return d->stringOffset; + + qWarning("QTextStream::pos: no device"); + return qint64(-1); +} + +/*! + Reads and discards whitespace from the stream until either a + non-space character is detected, or until atEnd() returns + true. This function is useful when reading a stream character by + character. + + Whitespace characters are all characters for which + QChar::isSpace() returns \c true. + + \sa operator>>() +*/ +void QTextStream::skipWhiteSpace() +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(Q_VOID); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); +} + +/*! + Sets the current device to \a device. If a device has already been + assigned, QTextStream will call flush() before the old device is + replaced. + + \note This function resets locale to the default locale ('C') + and codec to the default codec, QTextCodec::codecForLocale(). + + \sa device(), setString() +*/ +void QTextStream::setDevice(QIODevice *device) +{ + Q_D(QTextStream); + flush(); + if (d->deleteDevice) { +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.disconnect(); +#endif + delete d->device; + d->deleteDevice = false; + } + + d->reset(); + d->status = Ok; + d->device = device; + d->resetReadBuffer(); +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif +} + +/*! + Returns the current device associated with the QTextStream, + or 0 if no device has been assigned. + + \sa setDevice(), string() +*/ +QIODevice *QTextStream::device() const +{ + Q_D(const QTextStream); + return d->device; +} + +/*! + Sets the current string to \a string, using the given \a + openMode. If a device has already been assigned, QTextStream will + call flush() before replacing it. + + \sa string(), setDevice() +*/ +void QTextStream::setString(QString *string, QIODevice::OpenMode openMode) +{ + Q_D(QTextStream); + flush(); + if (d->deleteDevice) { +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.disconnect(); + d->device->blockSignals(true); +#endif + delete d->device; + d->deleteDevice = false; + } + + d->reset(); + d->status = Ok; + d->string = string; + d->stringOpenMode = openMode; +} + +/*! + Returns the current string assigned to the QTextStream, or 0 if no + string has been assigned. + + \sa setString(), device() +*/ +QString *QTextStream::string() const +{ + Q_D(const QTextStream); + return d->string; +} + +/*! + Sets the field alignment to \a mode. When used together with + setFieldWidth(), this function allows you to generate formatted + output with text aligned to the left, to the right or center + aligned. + + \sa fieldAlignment(), setFieldWidth() +*/ +void QTextStream::setFieldAlignment(FieldAlignment mode) +{ + Q_D(QTextStream); + d->params.fieldAlignment = mode; +} + +/*! + Returns the current field alignment. + + \sa setFieldAlignment(), fieldWidth() +*/ +QTextStream::FieldAlignment QTextStream::fieldAlignment() const +{ + Q_D(const QTextStream); + return d->params.fieldAlignment; +} + +/*! + Sets the pad character to \a ch. The default value is the ASCII + space character (' '), or QChar(0x20). This character is used to + fill in the space in fields when generating text. + + Example: + + \snippet code/src_corelib_io_qtextstream.cpp 5 + + The string \c s contains: + + \snippet code/src_corelib_io_qtextstream.cpp 6 + + \sa padChar(), setFieldWidth() +*/ +void QTextStream::setPadChar(QChar ch) +{ + Q_D(QTextStream); + d->params.padChar = ch; +} + +/*! + Returns the current pad character. + + \sa setPadChar(), setFieldWidth() +*/ +QChar QTextStream::padChar() const +{ + Q_D(const QTextStream); + return d->params.padChar; +} + +/*! + Sets the current field width to \a width. If \a width is 0 (the + default), the field width is equal to the length of the generated + text. + + \note The field width applies to every element appended to this + stream after this function has been called (e.g., it also pads + endl). This behavior is different from similar classes in the STL, + where the field width only applies to the next element. + + \sa fieldWidth(), setPadChar() +*/ +void QTextStream::setFieldWidth(int width) +{ + Q_D(QTextStream); + d->params.fieldWidth = width; +} + +/*! + Returns the current field width. + + \sa setFieldWidth() +*/ +int QTextStream::fieldWidth() const +{ + Q_D(const QTextStream); + return d->params.fieldWidth; +} + +/*! + Sets the current number flags to \a flags. \a flags is a set of + flags from the NumberFlag enum, and describes options for + formatting generated code (e.g., whether or not to always write + the base or sign of a number). + + \sa numberFlags(), setIntegerBase(), setRealNumberNotation() +*/ +void QTextStream::setNumberFlags(NumberFlags flags) +{ + Q_D(QTextStream); + d->params.numberFlags = flags; +} + +/*! + Returns the current number flags. + + \sa setNumberFlags(), integerBase(), realNumberNotation() +*/ +QTextStream::NumberFlags QTextStream::numberFlags() const +{ + Q_D(const QTextStream); + return d->params.numberFlags; +} + +/*! + Sets the base of integers to \a base, both for reading and for + generating numbers. \a base can be either 2 (binary), 8 (octal), + 10 (decimal) or 16 (hexadecimal). If \a base is 0, QTextStream + will attempt to detect the base by inspecting the data on the + stream. When generating numbers, QTextStream assumes base is 10 + unless the base has been set explicitly. + + \sa integerBase(), QString::number(), setNumberFlags() +*/ +void QTextStream::setIntegerBase(int base) +{ + Q_D(QTextStream); + d->params.integerBase = base; +} + +/*! + Returns the current base of integers. 0 means that the base is + detected when reading, or 10 (decimal) when generating numbers. + + \sa setIntegerBase(), QString::number(), numberFlags() +*/ +int QTextStream::integerBase() const +{ + Q_D(const QTextStream); + return d->params.integerBase; +} + +/*! + Sets the real number notation to \a notation (SmartNotation, + FixedNotation, ScientificNotation). When reading and generating + numbers, QTextStream uses this value to detect the formatting of + real numbers. + + \sa realNumberNotation(), setRealNumberPrecision(), setNumberFlags(), setIntegerBase() +*/ +void QTextStream::setRealNumberNotation(RealNumberNotation notation) +{ + Q_D(QTextStream); + d->params.realNumberNotation = notation; +} + +/*! + Returns the current real number notation. + + \sa setRealNumberNotation(), realNumberPrecision(), numberFlags(), integerBase() +*/ +QTextStream::RealNumberNotation QTextStream::realNumberNotation() const +{ + Q_D(const QTextStream); + return d->params.realNumberNotation; +} + +/*! + Sets the precision of real numbers to \a precision. This value + describes the number of fraction digits QTextStream should + write when generating real numbers. + + The precision cannot be a negative value. The default value is 6. + + \sa realNumberPrecision(), setRealNumberNotation() +*/ +void QTextStream::setRealNumberPrecision(int precision) +{ + Q_D(QTextStream); + if (precision < 0) { + qWarning("QTextStream::setRealNumberPrecision: Invalid precision (%d)", precision); + d->params.realNumberPrecision = 6; + return; + } + d->params.realNumberPrecision = precision; +} + +/*! + Returns the current real number precision, or the number of fraction + digits QTextStream will write when generating real numbers. + + \sa setRealNumberNotation(), realNumberNotation(), numberFlags(), integerBase() +*/ +int QTextStream::realNumberPrecision() const +{ + Q_D(const QTextStream); + return d->params.realNumberPrecision; +} + +/*! + Returns the status of the text stream. + + \sa QTextStream::Status, setStatus(), resetStatus() +*/ + +QTextStream::Status QTextStream::status() const +{ + Q_D(const QTextStream); + return d->status; +} + +/*! + \since 4.1 + + Resets the status of the text stream. + + \sa QTextStream::Status, status(), setStatus() +*/ +void QTextStream::resetStatus() +{ + Q_D(QTextStream); + d->status = Ok; +} + +/*! + \since 4.1 + + Sets the status of the text stream to the \a status given. + + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + + \sa Status, status(), resetStatus() +*/ +void QTextStream::setStatus(Status status) +{ + Q_D(QTextStream); + if (d->status == Ok) + d->status = status; +} + +/*! + Returns \c true if there is no more data to be read from the + QTextStream; otherwise returns \c false. This is similar to, but not + the same as calling QIODevice::atEnd(), as QTextStream also takes + into account its internal Unicode buffer. +*/ +bool QTextStream::atEnd() const +{ + Q_D(const QTextStream); + CHECK_VALID_STREAM(true); + + if (d->string) + return d->string->size() == d->stringOffset; + return d->readBuffer.isEmpty() && d->device->atEnd(); +} + +/*! + Reads the entire content of the stream, and returns it as a + QString. Avoid this function when working on large files, as it + will consume a significant amount of memory. + + Calling \l {QTextStream::readLine()}{readLine()} is better if you do not know how much data is + available. + + \sa readLine() +*/ +QString QTextStream::readAll() +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(QString()); + + return d->read(INT_MAX); +} + +/*! + Reads one line of text from the stream, and returns it as a + QString. The maximum allowed line length is set to \a maxlen. If + the stream contains lines longer than this, then the lines will be + split after \a maxlen characters and returned in parts. + + If \a maxlen is 0, the lines can be of any length. + + The returned line has no trailing end-of-line characters ("\\n" + or "\\r\\n"), so calling QString::trimmed() can be unnecessary. + + If the stream has read to the end of the file, \l {QTextStream::readLine()}{readLine()} + will return a null QString. For strings, or for devices that support it, + you can explicitly test for the end of the stream using atEnd(). + + \sa readAll(), QIODevice::readLine() +*/ +QString QTextStream::readLine(qint64 maxlen) +{ + QString line; + + readLineInto(&line, maxlen); + return line; +} + +/*! + \since 5.5 + + Reads one line of text from the stream into \a line. + If \a line is 0, the read line is not stored. + + The maximum allowed line length is set to \a maxlen. If + the stream contains lines longer than this, then the lines will be + split after \a maxlen characters and returned in parts. + + If \a maxlen is 0, the lines can be of any length. + + The resulting line has no trailing end-of-line characters ("\\n" + or "\\r\\n"), so calling QString::trimmed() can be unnecessary. + + If \a line has sufficient capacity for the data that is about to be + read, this function may not need to allocate new memory. Because of + this, it can be faster than readLine(). + + Returns \c false if the stream has read to the end of the file or + an error has occurred; otherwise returns \c true. The contents in + \a line before the call are discarded in any case. + + \sa readAll(), QIODevice::readLine() +*/ +bool QTextStream::readLineInto(QString *line, qint64 maxlen) +{ + Q_D(QTextStream); + // keep in sync with CHECK_VALID_STREAM + if (!d->string && !d->device) { + qWarning("QTextStream: No device"); + if (line && !line->isNull()) + line->resize(0); + return false; + } + + const QChar *readPtr; + int length; + if (!d->scan(&readPtr, &length, int(maxlen), QTextStreamPrivate::EndOfLine)) { + if (line && !line->isNull()) + line->resize(0); + return false; + } + + if (Q_LIKELY(line)) + line->setUnicode(readPtr, length); + d->consumeLastToken(); + return true; +} + +/*! + \since 4.1 + + Reads at most \a maxlen characters from the stream, and returns the data + read as a QString. + + \sa readAll(), readLine(), QIODevice::read() +*/ +QString QTextStream::read(qint64 maxlen) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(QString()); + + if (maxlen <= 0) + return QString::fromLatin1(""); // empty, not null + + return d->read(int(maxlen)); +} + +/*! + \internal +*/ +QTextStreamPrivate::NumberParsingStatus QTextStreamPrivate::getNumber(qulonglong *ret) +{ + scan(0, 0, 0, NotSpace); + consumeLastToken(); + + // detect int encoding + int base = params.integerBase; + if (base == 0) { + QChar ch; + if (!getChar(&ch)) + return npsInvalidPrefix; + if (ch == QLatin1Char('0')) { + QChar ch2; + if (!getChar(&ch2)) { + // Result is the number 0 + *ret = 0; + return npsOk; + } + ch2 = ch2.toLower(); + + if (ch2 == QLatin1Char('x')) { + base = 16; + } else if (ch2 == QLatin1Char('b')) { + base = 2; + } else if (ch2.isDigit() && ch2.digitValue() >= 0 && ch2.digitValue() <= 7) { + base = 8; + } else { + base = 10; + } + ungetChar(ch2); + } else if (ch == locale.negativeSign() || ch == locale.positiveSign() || ch.isDigit()) { + base = 10; + } else { + ungetChar(ch); + return npsInvalidPrefix; + } + ungetChar(ch); + // State of the stream is now the same as on entry + // (cursor is at prefix), + // and local variable 'base' has been set appropriately. + } + + qulonglong val=0; + switch (base) { + case 2: { + QChar pf1, pf2, dig; + // Parse prefix '0b' + if (!getChar(&pf1) || pf1 != QLatin1Char('0')) + return npsInvalidPrefix; + if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('b')) + return npsInvalidPrefix; + // Parse digits + int ndigits = 0; + while (getChar(&dig)) { + int n = dig.toLower().unicode(); + if (n == '0' || n == '1') { + val <<= 1; + val += n - '0'; + } else { + ungetChar(dig); + break; + } + ndigits++; + } + if (ndigits == 0) { + // Unwind the prefix and abort + ungetChar(pf2); + ungetChar(pf1); + return npsMissingDigit; + } + break; + } + case 8: { + QChar pf, dig; + // Parse prefix '0' + if (!getChar(&pf) || pf != QLatin1Char('0')) + return npsInvalidPrefix; + // Parse digits + int ndigits = 0; + while (getChar(&dig)) { + int n = dig.toLower().unicode(); + if (n >= '0' && n <= '7') { + val *= 8; + val += n - '0'; + } else { + ungetChar(dig); + break; + } + ndigits++; + } + if (ndigits == 0) { + // Unwind the prefix and abort + ungetChar(pf); + return npsMissingDigit; + } + break; + } + case 10: { + // Parse sign (or first digit) + QChar sign; + int ndigits = 0; + if (!getChar(&sign)) + return npsMissingDigit; + if (sign != locale.negativeSign() && sign != locale.positiveSign()) { + if (!sign.isDigit()) { + ungetChar(sign); + return npsMissingDigit; + } + val += sign.digitValue(); + ndigits++; + } + // Parse digits + QChar ch; + while (getChar(&ch)) { + if (ch.isDigit()) { + val *= 10; + val += ch.digitValue(); + } else if (locale != QLocale::c() && ch == locale.groupSeparator()) { + continue; + } else { + ungetChar(ch); + break; + } + ndigits++; + } + if (ndigits == 0) + return npsMissingDigit; + if (sign == locale.negativeSign()) { + qlonglong ival = qlonglong(val); + if (ival > 0) + ival = -ival; + val = qulonglong(ival); + } + break; + } + case 16: { + QChar pf1, pf2, dig; + // Parse prefix ' 0x' + if (!getChar(&pf1) || pf1 != QLatin1Char('0')) + return npsInvalidPrefix; + if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('x')) + return npsInvalidPrefix; + // Parse digits + int ndigits = 0; + while (getChar(&dig)) { + int n = dig.toLower().unicode(); + if (n >= '0' && n <= '9') { + val <<= 4; + val += n - '0'; + } else if (n >= 'a' && n <= 'f') { + val <<= 4; + val += 10 + (n - 'a'); + } else { + ungetChar(dig); + break; + } + ndigits++; + } + if (ndigits == 0) { + return npsMissingDigit; + } + break; + } + default: + // Unsupported integerBase + return npsInvalidPrefix; + } + + if (ret) + *ret = val; + return npsOk; +} + +/*! + \internal + (hihi) +*/ +bool QTextStreamPrivate::getReal(double *f) +{ + // We use a table-driven FSM to parse floating point numbers + // strtod() cannot be used directly since we may be reading from a + // QIODevice. + enum ParserState { + Init = 0, + Sign = 1, + Mantissa = 2, + Dot = 3, + Abscissa = 4, + ExpMark = 5, + ExpSign = 6, + Exponent = 7, + Nan1 = 8, + Nan2 = 9, + Inf1 = 10, + Inf2 = 11, + NanInf = 12, + Done = 13 + }; + enum InputToken { + None = 0, + InputSign = 1, + InputDigit = 2, + InputDot = 3, + InputExp = 4, + InputI = 5, + InputN = 6, + InputF = 7, + InputA = 8, + InputT = 9 + }; + + static const uchar table[13][10] = { + // None InputSign InputDigit InputDot InputExp InputI InputN InputF InputA InputT + { 0, Sign, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 0 Init + { 0, 0, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 1 Sign + { Done, Done, Mantissa, Dot, ExpMark, 0, 0, 0, 0, 0 }, // 2 Mantissa + { 0, 0, Abscissa, 0, 0, 0, 0, 0, 0, 0 }, // 3 Dot + { Done, Done, Abscissa, Done, ExpMark, 0, 0, 0, 0, 0 }, // 4 Abscissa + { 0, ExpSign, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 5 ExpMark + { 0, 0, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 6 ExpSign + { Done, Done, Exponent, Done, Done, 0, 0, 0, 0, 0 }, // 7 Exponent + { 0, 0, 0, 0, 0, 0, 0, 0, Nan2, 0 }, // 8 Nan1 + { 0, 0, 0, 0, 0, 0, NanInf, 0, 0, 0 }, // 9 Nan2 + { 0, 0, 0, 0, 0, 0, Inf2, 0, 0, 0 }, // 10 Inf1 + { 0, 0, 0, 0, 0, 0, 0, NanInf, 0, 0 }, // 11 Inf2 + { Done, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 11 NanInf + }; + + ParserState state = Init; + InputToken input = None; + + scan(0, 0, 0, NotSpace); + consumeLastToken(); + + const int BufferSize = 128; + char buf[BufferSize]; + int i = 0; + + QChar c; + while (getChar(&c)) { + switch (c.unicode()) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + input = InputDigit; + break; + case 'i': case 'I': + input = InputI; + break; + case 'n': case 'N': + input = InputN; + break; + case 'f': case 'F': + input = InputF; + break; + case 'a': case 'A': + input = InputA; + break; + case 't': case 'T': + input = InputT; + break; + default: { + QChar lc = c.toLower(); + if (lc == locale.decimalPoint().toLower()) + input = InputDot; + else if (lc == locale.exponential().toLower()) + input = InputExp; + else if (lc == locale.negativeSign().toLower() + || lc == locale.positiveSign().toLower()) + input = InputSign; + else if (locale != QLocale::c() // backward-compatibility + && lc == locale.groupSeparator().toLower()) + input = InputDigit; // well, it isn't a digit, but no one cares. + else + input = None; + } + break; + } + + state = ParserState(table[state][input]); + + if (state == Init || state == Done || i > (BufferSize - 5)) { + ungetChar(c); + if (i > (BufferSize - 5)) { // ignore rest of digits + while (getChar(&c)) { + if (!c.isDigit()) { + ungetChar(c); + break; + } + } + } + break; + } + + buf[i++] = c.toLatin1(); + } + + if (i == 0) + return false; + if (!f) + return true; + buf[i] = '\0'; + + // backward-compatibility. Old implementation supported +nan/-nan + // for some reason. QLocale only checks for lower-case + // nan/+inf/-inf, so here we also check for uppercase and mixed + // case versions. + if (!qstricmp(buf, "nan") || !qstricmp(buf, "+nan") || !qstricmp(buf, "-nan")) { + *f = qSNaN(); + return true; + } else if (!qstricmp(buf, "+inf") || !qstricmp(buf, "inf")) { + *f = qInf(); + return true; + } else if (!qstricmp(buf, "-inf")) { + *f = -qInf(); + return true; + } + bool ok; + *f = locale.toDouble(QString::fromLatin1(buf), &ok); + return ok; +} + +/*! + Reads a character from the stream and stores it in \a c. Returns a + reference to the QTextStream, so several operators can be + nested. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 7 + + Whitespace is \e not skipped. +*/ + +QTextStream &QTextStream::operator>>(QChar &c) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + if (!d->getChar(&c)) + setStatus(ReadPastEnd); + return *this; +} + +/*! + \overload + + Reads a character from the stream and stores it in \a c. The + character from the stream is converted to ISO-5589-1 before it is + stored. + + \sa QChar::toLatin1() +*/ +QTextStream &QTextStream::operator>>(char &c) +{ + QChar ch; + *this >> ch; + c = ch.toLatin1(); + return *this; +} + +/*! + Reads an integer from the stream and stores it in \a i, then + returns a reference to the QTextStream. The number is cast to + the correct type before it is stored. If no number was detected on + the stream, \a i is set to 0. + + By default, QTextStream will attempt to detect the base of the + number using the following rules: + + \table + \header \li Prefix \li Base + \row \li "0b" or "0B" \li 2 (binary) + \row \li "0" followed by "0-7" \li 8 (octal) + \row \li "0" otherwise \li 10 (decimal) + \row \li "0x" or "0X" \li 16 (hexadecimal) + \row \li "1" to "9" \li 10 (decimal) + \endtable + + By calling setIntegerBase(), you can specify the integer base + explicitly. This will disable the auto-detection, and speed up + QTextStream slightly. + + Leading whitespace is skipped. +*/ +QTextStream &QTextStream::operator>>(signed short &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed short); +} + +/*! + \overload + + Stores the integer in the unsigned short \a i. +*/ +QTextStream &QTextStream::operator>>(unsigned short &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned short); +} + +/*! + \overload + + Stores the integer in the signed int \a i. +*/ +QTextStream &QTextStream::operator>>(signed int &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed int); +} + +/*! + \overload + + Stores the integer in the unsigned int \a i. +*/ +QTextStream &QTextStream::operator>>(unsigned int &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned int); +} + +/*! + \overload + + Stores the integer in the signed long \a i. +*/ +QTextStream &QTextStream::operator>>(signed long &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed long); +} + +/*! + \overload + + Stores the integer in the unsigned long \a i. +*/ +QTextStream &QTextStream::operator>>(unsigned long &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned long); +} + +/*! + \overload + + Stores the integer in the qlonglong \a i. +*/ +QTextStream &QTextStream::operator>>(qlonglong &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qlonglong); +} + +/*! + \overload + + Stores the integer in the qulonglong \a i. +*/ +QTextStream &QTextStream::operator>>(qulonglong &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qulonglong); +} + +/*! + Reads a real number from the stream and stores it in \a f, then + returns a reference to the QTextStream. The number is cast to + the correct type. If no real number is detect on the stream, \a f + is set to 0.0. + + As a special exception, QTextStream allows the strings "nan" and "inf" to + represent NAN and INF floats or doubles. + + Leading whitespace is skipped. +*/ +QTextStream &QTextStream::operator>>(float &f) +{ + IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(float); +} + +/*! + \overload + + Stores the real number in the double \a f. +*/ +QTextStream &QTextStream::operator>>(double &f) +{ + IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(double); +} + +/*! + Reads a word from the stream and stores it in \a str, then returns + a reference to the stream. Words are separated by whitespace + (i.e., all characters for which QChar::isSpace() returns \c true). + + Leading whitespace is skipped. +*/ +QTextStream &QTextStream::operator>>(QString &str) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + + str.clear(); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); + + const QChar *ptr; + int length; + if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { + setStatus(ReadPastEnd); + return *this; + } + + str = QString(ptr, length); + d->consumeLastToken(); + return *this; +} + +/*! + \overload + + Converts the word to ISO-8859-1, then stores it in \a array. + + \sa QString::toLatin1() +*/ +QTextStream &QTextStream::operator>>(QByteArray &array) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + + array.clear(); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); + + const QChar *ptr; + int length; + if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { + setStatus(ReadPastEnd); + return *this; + } + + for (int i = 0; i < length; ++i) + array += ptr[i].toLatin1(); + + d->consumeLastToken(); + return *this; +} + +/*! + \overload + + Stores the word in \a c, terminated by a '\\0' character. If no word is + available, only the '\\0' character is stored. + + Warning: Although convenient, this operator is dangerous and must + be used with care. QTextStream assumes that \a c points to a + buffer with enough space to hold the word. If the buffer is too + small, your application may crash. + + If possible, use the QByteArray operator instead. +*/ +QTextStream &QTextStream::operator>>(char *c) +{ + Q_D(QTextStream); + *c = 0; + CHECK_VALID_STREAM(*this); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); + + const QChar *ptr; + int length; + if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { + setStatus(ReadPastEnd); + return *this; + } + + for (int i = 0; i < length; ++i) + *c++ = ptr[i].toLatin1(); + *c = '\0'; + d->consumeLastToken(); + return *this; +} + +/*! + \internal + */ +void QTextStreamPrivate::putNumber(qulonglong number, bool negative) +{ + QString result; + + unsigned flags = 0; + const QTextStream::NumberFlags numberFlags = params.numberFlags; + if (numberFlags & QTextStream::ShowBase) + flags |= QLocaleData::ShowBase; + if (numberFlags & QTextStream::ForceSign) + flags |= QLocaleData::AlwaysShowSign; + if (numberFlags & QTextStream::UppercaseBase) + flags |= QLocaleData::UppercaseBase; + if (numberFlags & QTextStream::UppercaseDigits) + flags |= QLocaleData::CapitalEorX; + + // add thousands group separators. For backward compatibility we + // don't add a group separator for C locale. + if (locale != QLocale::c() && !locale.numberOptions().testFlag(QLocale::OmitGroupSeparator)) + flags |= QLocaleData::ThousandsGroup; + + const QLocaleData *dd = locale.d->m_data; + int base = params.integerBase ? params.integerBase : 10; + if (negative && base == 10) { + result = dd->longLongToString(-static_cast<qlonglong>(number), -1, + base, -1, flags); + } else if (negative) { + // Workaround for backward compatibility for writing negative + // numbers in octal and hex: + // QTextStream(result) << showbase << hex << -1 << oct << -1 + // should output: -0x1 -0b1 + result = dd->unsLongLongToString(number, -1, base, -1, flags); + result.prepend(locale.negativeSign()); + } else { + result = dd->unsLongLongToString(number, -1, base, -1, flags); + // workaround for backward compatibility - in octal form with + // ShowBase flag set zero should be written as '00' + if (number == 0 && base == 8 && params.numberFlags & QTextStream::ShowBase + && result == QLatin1String("0")) { + result.prepend(QLatin1Char('0')); + } + } + putString(result, true); +} + +/*! + Writes the character \a c to the stream, then returns a reference + to the QTextStream. + + \sa setFieldWidth() +*/ +QTextStream &QTextStream::operator<<(QChar c) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putChar(c); + return *this; +} + +/*! + \overload + + Converts \a c from ASCII to a QChar, then writes it to the stream. +*/ +QTextStream &QTextStream::operator<<(char c) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putChar(QChar::fromLatin1(c)); + return *this; +} + +/*! + Writes the integer number \a i to the stream, then returns a + reference to the QTextStream. By default, the number is stored in + decimal form, but you can also set the base by calling + setIntegerBase(). + + \sa setFieldWidth(), setNumberFlags() +*/ +QTextStream &QTextStream::operator<<(signed short i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); + return *this; +} + +/*! + \overload + + Writes the unsigned short \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(unsigned short i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)i, false); + return *this; +} + +/*! + \overload + + Writes the signed int \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(signed int i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); + return *this; +} + +/*! + \overload + + Writes the unsigned int \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(unsigned int i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)i, false); + return *this; +} + +/*! + \overload + + Writes the signed long \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(signed long i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); + return *this; +} + +/*! + \overload + + Writes the unsigned long \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(unsigned long i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)i, false); + return *this; +} + +/*! + \overload + + Writes the qlonglong \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(qlonglong i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(i), i < 0); + return *this; +} + +/*! + \overload + + Writes the qulonglong \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(qulonglong i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber(i, false); + return *this; +} + +/*! + Writes the real number \a f to the stream, then returns a + reference to the QTextStream. By default, QTextStream stores it + using SmartNotation, with up to 6 digits of precision. You can + change the textual representation QTextStream will use for real + numbers by calling setRealNumberNotation(), + setRealNumberPrecision() and setNumberFlags(). + + \sa setFieldWidth(), setRealNumberNotation(), + setRealNumberPrecision(), setNumberFlags() +*/ +QTextStream &QTextStream::operator<<(float f) +{ + return *this << double(f); +} + +/*! + \overload + + Writes the double \a f to the stream. +*/ +QTextStream &QTextStream::operator<<(double f) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + + QLocaleData::DoubleForm form = QLocaleData::DFDecimal; + switch (realNumberNotation()) { + case FixedNotation: + form = QLocaleData::DFDecimal; + break; + case ScientificNotation: + form = QLocaleData::DFExponent; + break; + case SmartNotation: + form = QLocaleData::DFSignificantDigits; + break; + } + + uint flags = 0; + const QLocale::NumberOptions numberOptions = locale().numberOptions(); + if (numberFlags() & ShowBase) + flags |= QLocaleData::ShowBase; + if (numberFlags() & ForceSign) + flags |= QLocaleData::AlwaysShowSign; + if (numberFlags() & UppercaseBase) + flags |= QLocaleData::UppercaseBase; + if (numberFlags() & UppercaseDigits) + flags |= QLocaleData::CapitalEorX; + if (numberFlags() & ForcePoint) { + flags |= QLocaleData::ForcePoint; + + // Only for backwards compatibility + flags |= QLocaleData::AddTrailingZeroes | QLocaleData::ShowBase; + } + if (locale() != QLocale::c() && !(numberOptions & QLocale::OmitGroupSeparator)) + flags |= QLocaleData::ThousandsGroup; + if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) + flags |= QLocaleData::ZeroPadExponent; + if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; + + const QLocaleData *dd = d->locale.d->m_data; + QString num = dd->doubleToString(f, d->params.realNumberPrecision, form, -1, flags); + d->putString(num, true); + return *this; +} + +/*! + Writes the string \a string to the stream, and returns a reference + to the QTextStream. The string is first encoded using the assigned + codec (the default codec is QTextCodec::codecForLocale()) before + it is written to the stream. + + \sa setFieldWidth(), setCodec() +*/ +QTextStream &QTextStream::operator<<(const QString &string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(string); + return *this; +} + +/*! + \overload + + Writes \a string to the stream, and returns a reference to the + QTextStream. +*/ +QTextStream &QTextStream::operator<<(QLatin1String string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(string); + return *this; +} + +/*! + \since 5.6 + \overload + + Writes \a string to the stream, and returns a reference to the + QTextStream. +*/ +QTextStream &QTextStream::operator<<(const QStringRef &string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(string.data(), string.size()); + return *this; +} + +/*! + \overload + + Writes \a array to the stream. The contents of \a array are + converted with QString::fromUtf8(). +*/ +QTextStream &QTextStream::operator<<(const QByteArray &array) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(QString::fromUtf8(array.constData(), array.length())); + return *this; +} + +/*! + \overload + + Writes the constant string pointed to by \a string to the stream. \a + string is assumed to be in ISO-8859-1 encoding. This operator + is convenient when working with constant string data. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 8 + + Warning: QTextStream assumes that \a string points to a string of + text, terminated by a '\\0' character. If there is no terminating + '\\0' character, your application may crash. +*/ +QTextStream &QTextStream::operator<<(const char *string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + // ### Qt6: consider changing to UTF-8 + d->putString(QLatin1String(string)); + return *this; +} + +/*! + \overload + + Writes \a ptr to the stream as a hexadecimal number with a base. +*/ + +QTextStream &QTextStream::operator<<(const void *ptr) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + const int oldBase = d->params.integerBase; + const NumberFlags oldFlags = d->params.numberFlags; + d->params.integerBase = 16; + d->params.numberFlags |= ShowBase; + d->putNumber(reinterpret_cast<quintptr>(ptr), false); + d->params.integerBase = oldBase; + d->params.numberFlags = oldFlags; + return *this; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(2) on \a stream and returns \a + stream. + + \sa oct(), dec(), hex(), {QTextStream manipulators} +*/ +QTextStream &bin(QTextStream &stream) +{ + stream.setIntegerBase(2); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(8) on \a stream and returns \a + stream. + + \sa bin(), dec(), hex(), {QTextStream manipulators} +*/ +QTextStream &oct(QTextStream &stream) +{ + stream.setIntegerBase(8); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(10) on \a stream and returns \a + stream. + + \sa bin(), oct(), hex(), {QTextStream manipulators} +*/ +QTextStream &dec(QTextStream &stream) +{ + stream.setIntegerBase(10); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(16) on \a stream and returns \a + stream. + + \note The hex modifier can only be used for writing to streams. + \sa bin(), oct(), dec(), {QTextStream manipulators} +*/ +QTextStream &hex(QTextStream &stream) +{ + stream.setIntegerBase(16); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::ShowBase) on \a stream and returns \a stream. + + \sa noshowbase(), forcesign(), forcepoint(), {QTextStream manipulators} +*/ +QTextStream &showbase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::ShowBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::ForceSign) on \a stream and returns \a stream. + + \sa noforcesign(), forcepoint(), showbase(), {QTextStream manipulators} +*/ +QTextStream &forcesign(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::ForceSign); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::ForcePoint) on \a stream and returns \a stream. + + \sa noforcepoint(), forcesign(), showbase(), {QTextStream manipulators} +*/ +QTextStream &forcepoint(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::ForcePoint); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::ShowBase) on \a stream and returns \a stream. + + \sa showbase(), noforcesign(), noforcepoint(), {QTextStream manipulators} +*/ +QTextStream &noshowbase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ShowBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::ForceSign) on \a stream and returns \a stream. + + \sa forcesign(), noforcepoint(), noshowbase(), {QTextStream manipulators} +*/ +QTextStream &noforcesign(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForceSign); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::ForcePoint) on \a stream and returns \a stream. + + \sa forcepoint(), noforcesign(), noshowbase(), {QTextStream manipulators} +*/ +QTextStream &noforcepoint(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForcePoint); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::UppercaseBase) on \a stream and returns \a stream. + + \sa lowercasebase(), uppercasedigits(), {QTextStream manipulators} +*/ +QTextStream &uppercasebase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::UppercaseDigits) on \a stream and returns \a stream. + + \sa lowercasedigits(), uppercasebase(), {QTextStream manipulators} +*/ +QTextStream &uppercasedigits(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseDigits); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::UppercaseBase) on \a stream and returns \a stream. + + \sa uppercasebase(), lowercasedigits(), {QTextStream manipulators} +*/ +QTextStream &lowercasebase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::UppercaseDigits) on \a stream and returns \a stream. + + \sa uppercasedigits(), lowercasebase(), {QTextStream manipulators} +*/ +QTextStream &lowercasedigits(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseDigits); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setRealNumberNotation(QTextStream::FixedNotation) + on \a stream and returns \a stream. + + \sa scientific(), {QTextStream manipulators} +*/ +QTextStream &fixed(QTextStream &stream) +{ + stream.setRealNumberNotation(QTextStream::FixedNotation); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setRealNumberNotation(QTextStream::ScientificNotation) + on \a stream and returns \a stream. + + \sa fixed(), {QTextStream manipulators} +*/ +QTextStream &scientific(QTextStream &stream) +{ + stream.setRealNumberNotation(QTextStream::ScientificNotation); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setFieldAlignment(QTextStream::AlignLeft) + on \a stream and returns \a stream. + + \sa {QTextStream::}{right()}, {QTextStream::}{center()}, {QTextStream manipulators} +*/ +QTextStream &left(QTextStream &stream) +{ + stream.setFieldAlignment(QTextStream::AlignLeft); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setFieldAlignment(QTextStream::AlignRight) + on \a stream and returns \a stream. + + \sa {QTextStream::}{left()}, {QTextStream::}{center()}, {QTextStream manipulators} +*/ +QTextStream &right(QTextStream &stream) +{ + stream.setFieldAlignment(QTextStream::AlignRight); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) + on \a stream and returns \a stream. + + \sa {QTextStream::}{left()}, {QTextStream::}{right()}, {QTextStream manipulators} +*/ +QTextStream ¢er(QTextStream &stream) +{ + stream.setFieldAlignment(QTextStream::AlignCenter); + return stream; +} + +/*! + \relates QTextStream + + Writes '\\n' to the \a stream and flushes the stream. + + Equivalent to + + \snippet code/src_corelib_io_qtextstream.cpp 9 + + Note: On Windows, all '\\n' characters are written as '\\r\\n' if + QTextStream's device or string is opened using the QIODevice::Text flag. + + \sa flush(), reset(), {QTextStream manipulators} +*/ +QTextStream &endl(QTextStream &stream) +{ + return stream << QLatin1Char('\n') << flush; +} + +/*! + \relates QTextStream + + Calls QTextStream::flush() on \a stream and returns \a stream. + + \sa endl(), reset(), {QTextStream manipulators} +*/ +QTextStream &flush(QTextStream &stream) +{ + stream.flush(); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::reset() on \a stream and returns \a stream. + + \sa flush(), {QTextStream manipulators} +*/ +QTextStream &reset(QTextStream &stream) +{ + stream.reset(); + return stream; +} + +/*! + \relates QTextStream + + Calls \l {QTextStream::}{skipWhiteSpace()} on \a stream and returns \a stream. + + \sa {QTextStream manipulators} +*/ +QTextStream &ws(QTextStream &stream) +{ + stream.skipWhiteSpace(); + return stream; +} + +/*! + \fn QTextStreamManipulator qSetFieldWidth(int width) + \relates QTextStream + + Equivalent to QTextStream::setFieldWidth(\a width). +*/ + +/*! + \fn QTextStreamManipulator qSetPadChar(QChar ch) + \relates QTextStream + + Equivalent to QTextStream::setPadChar(\a ch). +*/ + +/*! + \fn QTextStreamManipulator qSetRealNumberPrecision(int precision) + \relates QTextStream + + Equivalent to QTextStream::setRealNumberPrecision(\a precision). +*/ + +#ifndef QT_NO_TEXTCODEC +/*! + \relates QTextStream + + Toggles insertion of the Byte Order Mark on \a stream when QTextStream is + used with a UTF codec. + + \sa QTextStream::setGenerateByteOrderMark(), {QTextStream manipulators} +*/ +QTextStream &bom(QTextStream &stream) +{ + stream.setGenerateByteOrderMark(true); + return stream; +} + +/*! + Sets the codec for this stream to \a codec. The codec is used for + decoding any data that is read from the assigned device, and for + encoding any data that is written. By default, + QTextCodec::codecForLocale() is used, and automatic unicode + detection is enabled. + + If QTextStream operates on a string, this function does nothing. + + \warning If you call this function while the text stream is reading + from an open sequential socket, the internal buffer may still contain + text decoded using the old codec. + + \sa codec(), setAutoDetectUnicode(), setLocale() +*/ +void QTextStream::setCodec(QTextCodec *codec) +{ + Q_D(QTextStream); + qint64 seekPos = -1; + if (!d->readBuffer.isEmpty()) { + if (!d->device->isSequential()) { + seekPos = pos(); + } + } + d->codec = codec; + if (seekPos >=0 && !d->readBuffer.isEmpty()) + seek(seekPos); +} + +/*! + Sets the codec for this stream to the QTextCodec for the encoding + specified by \a codecName. Common values for \c codecName include + "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't + recognized, nothing happens. + + Example: + + \snippet code/src_corelib_io_qtextstream.cpp 10 + + \sa QTextCodec::codecForName(), setLocale() +*/ +void QTextStream::setCodec(const char *codecName) +{ + QTextCodec *codec = QTextCodec::codecForName(codecName); + if (codec) + setCodec(codec); +} + +/*! + Returns the codec that is current assigned to the stream. + + \sa setCodec(), setAutoDetectUnicode(), locale() +*/ +QTextCodec *QTextStream::codec() const +{ + Q_D(const QTextStream); + return d->codec; +} + +/*! + If \a enabled is true, QTextStream will attempt to detect Unicode + encoding by peeking into the stream data to see if it can find the + UTF-16 or UTF-32 BOM (Byte Order Mark). If this mark is found, QTextStream + will replace the current codec with the UTF codec. + + This function can be used together with setCodec(). It is common + to set the codec to UTF-8, and then enable UTF-16 detection. + + \sa autoDetectUnicode(), setCodec() +*/ +void QTextStream::setAutoDetectUnicode(bool enabled) +{ + Q_D(QTextStream); + d->autoDetectUnicode = enabled; +} + +/*! + Returns \c true if automatic Unicode detection is enabled, otherwise + returns \c false. Automatic Unicode detection is enabled by default. + + \sa setAutoDetectUnicode(), setCodec() +*/ +bool QTextStream::autoDetectUnicode() const +{ + Q_D(const QTextStream); + return d->autoDetectUnicode; +} + +/*! + If \a generate is true and a UTF codec is used, QTextStream will insert + the BOM (Byte Order Mark) before any data has been written to the + device. If \a generate is false, no BOM will be inserted. This function + must be called before any data is written. Otherwise, it does nothing. + + \sa generateByteOrderMark(), bom() +*/ +void QTextStream::setGenerateByteOrderMark(bool generate) +{ + Q_D(QTextStream); + if (d->writeBuffer.isEmpty()) { + d->writeConverterState.flags.setFlag(QTextCodec::IgnoreHeader, !generate); + } +} + +/*! + Returns \c true if QTextStream is set to generate the UTF BOM (Byte Order + Mark) when using a UTF codec; otherwise returns \c false. UTF BOM generation is + set to false by default. + + \sa setGenerateByteOrderMark() +*/ +bool QTextStream::generateByteOrderMark() const +{ + Q_D(const QTextStream); + return (d->writeConverterState.flags & QTextCodec::IgnoreHeader) == 0; +} + +#endif + +/*! + \since 4.5 + + Sets the locale for this stream to \a locale. The specified locale is + used for conversions between numbers and their string representations. + + The default locale is C and it is a special case - the thousands + group separator is not used for backward compatibility reasons. + + \sa locale() +*/ +void QTextStream::setLocale(const QLocale &locale) +{ + Q_D(QTextStream); + d->locale = locale; +} + +/*! + \since 4.5 + + Returns the locale for this stream. The default locale is C. + + \sa setLocale() +*/ +QLocale QTextStream::locale() const +{ + Q_D(const QTextStream); + return d->locale; +} + +QT_END_NAMESPACE + +#ifndef QT_NO_QOBJECT +#include "moc_qtextstream_p.cpp" +#endif diff --git a/src/corelib/serialization/qtextstream.h b/src/corelib/serialization/qtextstream.h new file mode 100644 index 0000000000..ee0b09419d --- /dev/null +++ b/src/corelib/serialization/qtextstream.h @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEXTSTREAM_H +#define QTEXTSTREAM_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qstring.h> +#include <QtCore/qchar.h> +#include <QtCore/qlocale.h> +#include <QtCore/qscopedpointer.h> + +#include <stdio.h> + +#ifdef Status +#error qtextstream.h must be included before any header file that defines Status +#endif + +QT_BEGIN_NAMESPACE + + +class QTextCodec; +class QTextDecoder; + +class QTextStreamPrivate; +class Q_CORE_EXPORT QTextStream // text stream class +{ + Q_DECLARE_PRIVATE(QTextStream) + +public: + enum RealNumberNotation { + SmartNotation, + FixedNotation, + ScientificNotation + }; + enum FieldAlignment { + AlignLeft, + AlignRight, + AlignCenter, + AlignAccountingStyle + }; + enum Status { + Ok, + ReadPastEnd, + ReadCorruptData, + WriteFailed + }; + enum NumberFlag { + ShowBase = 0x1, + ForcePoint = 0x2, + ForceSign = 0x4, + UppercaseBase = 0x8, + UppercaseDigits = 0x10 + }; + Q_DECLARE_FLAGS(NumberFlags, NumberFlag) + + QTextStream(); + explicit QTextStream(QIODevice *device); + explicit QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit QTextStream(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit QTextStream(QByteArray *array, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit QTextStream(const QByteArray &array, QIODevice::OpenMode openMode = QIODevice::ReadOnly); + virtual ~QTextStream(); + +#ifndef QT_NO_TEXTCODEC + void setCodec(QTextCodec *codec); + void setCodec(const char *codecName); + QTextCodec *codec() const; + void setAutoDetectUnicode(bool enabled); + bool autoDetectUnicode() const; + void setGenerateByteOrderMark(bool generate); + bool generateByteOrderMark() const; +#endif + + void setLocale(const QLocale &locale); + QLocale locale() const; + + void setDevice(QIODevice *device); + QIODevice *device() const; + + void setString(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + QString *string() const; + + Status status() const; + void setStatus(Status status); + void resetStatus(); + + bool atEnd() const; + void reset(); + void flush(); + bool seek(qint64 pos); + qint64 pos() const; + + void skipWhiteSpace(); + + QString readLine(qint64 maxlen = 0); + bool readLineInto(QString *line, qint64 maxlen = 0); + QString readAll(); + QString read(qint64 maxlen); + + void setFieldAlignment(FieldAlignment alignment); + FieldAlignment fieldAlignment() const; + + void setPadChar(QChar ch); + QChar padChar() const; + + void setFieldWidth(int width); + int fieldWidth() const; + + void setNumberFlags(NumberFlags flags); + NumberFlags numberFlags() const; + + void setIntegerBase(int base); + int integerBase() const; + + void setRealNumberNotation(RealNumberNotation notation); + RealNumberNotation realNumberNotation() const; + + void setRealNumberPrecision(int precision); + int realNumberPrecision() const; + + QTextStream &operator>>(QChar &ch); + QTextStream &operator>>(char &ch); + QTextStream &operator>>(signed short &i); + QTextStream &operator>>(unsigned short &i); + QTextStream &operator>>(signed int &i); + QTextStream &operator>>(unsigned int &i); + QTextStream &operator>>(signed long &i); + QTextStream &operator>>(unsigned long &i); + QTextStream &operator>>(qlonglong &i); + QTextStream &operator>>(qulonglong &i); + QTextStream &operator>>(float &f); + QTextStream &operator>>(double &f); + QTextStream &operator>>(QString &s); + QTextStream &operator>>(QByteArray &array); + QTextStream &operator>>(char *c); + + QTextStream &operator<<(QChar ch); + QTextStream &operator<<(char ch); + QTextStream &operator<<(signed short i); + QTextStream &operator<<(unsigned short i); + QTextStream &operator<<(signed int i); + QTextStream &operator<<(unsigned int i); + QTextStream &operator<<(signed long i); + QTextStream &operator<<(unsigned long i); + QTextStream &operator<<(qlonglong i); + QTextStream &operator<<(qulonglong i); + QTextStream &operator<<(float f); + QTextStream &operator<<(double f); + QTextStream &operator<<(const QString &s); + QTextStream &operator<<(QLatin1String s); + QTextStream &operator<<(const QStringRef &s); + QTextStream &operator<<(const QByteArray &array); + QTextStream &operator<<(const char *c); + QTextStream &operator<<(const void *ptr); + +private: + Q_DISABLE_COPY(QTextStream) + friend class QDebugStateSaverPrivate; + friend class QDebug; + + QScopedPointer<QTextStreamPrivate> d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QTextStream::NumberFlags) + +/***************************************************************************** + QTextStream manipulators + *****************************************************************************/ + +typedef QTextStream & (*QTextStreamFunction)(QTextStream &);// manipulator function +typedef void (QTextStream::*QTSMFI)(int); // manipulator w/int argument +typedef void (QTextStream::*QTSMFC)(QChar); // manipulator w/QChar argument + + +class Q_CORE_EXPORT QTextStreamManipulator +{ +public: + Q_DECL_CONSTEXPR QTextStreamManipulator(QTSMFI m, int a) Q_DECL_NOTHROW : mf(m), mc(nullptr), arg(a), ch() {} + Q_DECL_CONSTEXPR QTextStreamManipulator(QTSMFC m, QChar c) Q_DECL_NOTHROW : mf(nullptr), mc(m), arg(-1), ch(c) {} + void exec(QTextStream &s) { if (mf) { (s.*mf)(arg); } else { (s.*mc)(ch); } } + +private: + QTSMFI mf; // QTextStream member function + QTSMFC mc; // QTextStream member function + int arg; // member function argument + QChar ch; +}; + +inline QTextStream &operator>>(QTextStream &s, QTextStreamFunction f) +{ return (*f)(s); } + +inline QTextStream &operator<<(QTextStream &s, QTextStreamFunction f) +{ return (*f)(s); } + +inline QTextStream &operator<<(QTextStream &s, QTextStreamManipulator m) +{ m.exec(s); return s; } + +Q_CORE_EXPORT QTextStream &bin(QTextStream &s); +Q_CORE_EXPORT QTextStream &oct(QTextStream &s); +Q_CORE_EXPORT QTextStream &dec(QTextStream &s); +Q_CORE_EXPORT QTextStream &hex(QTextStream &s); + +Q_CORE_EXPORT QTextStream &showbase(QTextStream &s); +Q_CORE_EXPORT QTextStream &forcesign(QTextStream &s); +Q_CORE_EXPORT QTextStream &forcepoint(QTextStream &s); +Q_CORE_EXPORT QTextStream &noshowbase(QTextStream &s); +Q_CORE_EXPORT QTextStream &noforcesign(QTextStream &s); +Q_CORE_EXPORT QTextStream &noforcepoint(QTextStream &s); + +Q_CORE_EXPORT QTextStream &uppercasebase(QTextStream &s); +Q_CORE_EXPORT QTextStream &uppercasedigits(QTextStream &s); +Q_CORE_EXPORT QTextStream &lowercasebase(QTextStream &s); +Q_CORE_EXPORT QTextStream &lowercasedigits(QTextStream &s); + +Q_CORE_EXPORT QTextStream &fixed(QTextStream &s); +Q_CORE_EXPORT QTextStream &scientific(QTextStream &s); + +Q_CORE_EXPORT QTextStream &left(QTextStream &s); +Q_CORE_EXPORT QTextStream &right(QTextStream &s); +Q_CORE_EXPORT QTextStream ¢er(QTextStream &s); + +Q_CORE_EXPORT QTextStream &endl(QTextStream &s); +Q_CORE_EXPORT QTextStream &flush(QTextStream &s); +Q_CORE_EXPORT QTextStream &reset(QTextStream &s); + +Q_CORE_EXPORT QTextStream &bom(QTextStream &s); + +Q_CORE_EXPORT QTextStream &ws(QTextStream &s); + +inline QTextStreamManipulator qSetFieldWidth(int width) +{ + QTSMFI func = &QTextStream::setFieldWidth; + return QTextStreamManipulator(func,width); +} + +inline QTextStreamManipulator qSetPadChar(QChar ch) +{ + QTSMFC func = &QTextStream::setPadChar; + return QTextStreamManipulator(func, ch); +} + +inline QTextStreamManipulator qSetRealNumberPrecision(int precision) +{ + QTSMFI func = &QTextStream::setRealNumberPrecision; + return QTextStreamManipulator(func, precision); +} + +QT_END_NAMESPACE + +#endif // QTEXTSTREAM_H diff --git a/src/corelib/serialization/qtextstream_p.h b/src/corelib/serialization/qtextstream_p.h new file mode 100644 index 0000000000..a642beddc4 --- /dev/null +++ b/src/corelib/serialization/qtextstream_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEXTSTREAM_P_H +#define QTEXTSTREAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include "qtextstream.h" +#ifndef QT_NO_TEXTCODEC +#include "qtextcodec.h" +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QOBJECT +class QDeviceClosedNotifier : public QObject +{ + Q_OBJECT +public: + inline QDeviceClosedNotifier() + { } + + inline void setupDevice(QTextStream *stream, QIODevice *device) + { + disconnect(); + if (device) + connect(device, SIGNAL(aboutToClose()), this, SLOT(flushStream())); + this->stream = stream; + } + +public Q_SLOTS: + inline void flushStream() { stream->flush(); } + +private: + QTextStream *stream; +}; +#endif + +class QTextStreamPrivate +{ + Q_DECLARE_PUBLIC(QTextStream) +public: + // streaming parameters + class Params + { + public: + void reset(); + + int realNumberPrecision; + int integerBase; + int fieldWidth; + QChar padChar; + QTextStream::FieldAlignment fieldAlignment; + QTextStream::RealNumberNotation realNumberNotation; + QTextStream::NumberFlags numberFlags; + }; + + QTextStreamPrivate(QTextStream *q_ptr); + ~QTextStreamPrivate(); + void reset(); + + // device + QIODevice *device; +#ifndef QT_NO_QOBJECT + QDeviceClosedNotifier deviceClosedNotifier; +#endif + + // string + QString *string; + int stringOffset; + QIODevice::OpenMode stringOpenMode; + +#ifndef QT_NO_TEXTCODEC + // codec + QTextCodec *codec; + QTextCodec::ConverterState readConverterState; + QTextCodec::ConverterState writeConverterState; + QTextCodec::ConverterState *readConverterSavedState; +#endif + + QString writeBuffer; + QString readBuffer; + int readBufferOffset; + int readConverterSavedStateOffset; //the offset between readBufferStartDevicePos and that start of the buffer + qint64 readBufferStartDevicePos; + + Params params; + + // status + QTextStream::Status status; + QLocale locale; + QTextStream *q_ptr; + + int lastTokenSize; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + bool autoDetectUnicode; +#endif + + // i/o + enum TokenDelimiter { + Space, + NotSpace, + EndOfLine + }; + + QString read(int maxlen); + bool scan(const QChar **ptr, int *tokenLength, + int maxlen, TokenDelimiter delimiter); + inline const QChar *readPtr() const; + inline void consumeLastToken(); + inline void consume(int nchars); + void saveConverterState(qint64 newPos); + void restoreToSavedConverterState(); + + // Return value type for getNumber() + enum NumberParsingStatus { + npsOk, + npsMissingDigit, + npsInvalidPrefix + }; + + inline bool getChar(QChar *ch); + inline void ungetChar(QChar ch); + NumberParsingStatus getNumber(qulonglong *l); + bool getReal(double *f); + + inline void write(const QString &data) { write(data.begin(), data.length()); } + inline void write(QChar ch); + void write(const QChar *data, int len); + void write(QLatin1String data); + void writePadding(int len); + inline void putString(const QString &ch, bool number = false) { putString(ch.constData(), ch.length(), number); } + void putString(const QChar *data, int len, bool number = false); + void putString(QLatin1String data, bool number = false); + inline void putChar(QChar ch); + void putNumber(qulonglong number, bool negative); + + struct PaddingResult { + int left, right; + }; + PaddingResult padding(int len) const; + + // buffers + bool fillReadBuffer(qint64 maxBytes = -1); + void resetReadBuffer(); + void flushWriteBuffer(); +}; + +QT_END_NAMESPACE + +#endif // QTEXTSTREAM_P_H diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp new file mode 100644 index 0000000000..a92dd71df5 --- /dev/null +++ b/src/corelib/serialization/qxmlstream.cpp @@ -0,0 +1,4034 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtCore/qxmlstream.h" + +#ifndef QT_NO_XMLSTREAM + +#include "qxmlutils_p.h" +#include <qdebug.h> +#include <qfile.h> +#include <stdio.h> +#include <qtextcodec.h> +#include <qstack.h> +#include <qbuffer.h> +#ifndef QT_BOOTSTRAPPED +#include <qcoreapplication.h> +#else +// This specialization of Q_DECLARE_TR_FUNCTIONS is not in qcoreapplication.h, +// because that header depends on QObject being available, which is not the +// case for most bootstrapped applications. +#define Q_DECLARE_TR_FUNCTIONS(context) \ +public: \ + static inline QString tr(const char *sourceText, const char *comment = 0) \ + { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ + static inline QString trUtf8(const char *sourceText, const char *comment = 0) \ + { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ + static inline QString tr(const char *sourceText, const char*, int) \ + { return QString::fromLatin1(sourceText); } \ + static inline QString trUtf8(const char *sourceText, const char*, int) \ + { return QString::fromLatin1(sourceText); } \ +private: +#endif +QT_BEGIN_NAMESPACE + +#include "qxmlstream_p.h" + +enum { StreamEOF = ~0U }; + +/*! + \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 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 + 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 + */ + +/*! + Destroys the entity resolver. + */ +QXmlStreamEntityResolver::~QXmlStreamEntityResolver() +{ +} + +/*! + \internal + +This function is a stub for later functionality. +*/ +QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/) +{ + return QString(); +} + + +/*! + Resolves the undeclared entity \a name and returns its replacement + text. If the entity is also unknown to the entity resolver, it + returns an empty string. + + The default implementation always returns an empty string. +*/ + +QString QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/) +{ + return QString(); +} + +#ifndef QT_NO_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 0 is set. + + \sa entityResolver() + */ +void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver) +{ + Q_D(QXmlStreamReader); + d->entityResolver = resolver; +} + +/*! + \since 4.4 + + Returns the entity resolver, or 0 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 via a simple streaming API. + + + \ingroup xml-tools + + QXmlStreamReader is a faster and more convenient replacement for + Qt's own SAX parser (see QXmlSimpleReader). In some cases it might + also be a faster and more convenient alternative for use in + applications that would otherwise use a DOM tree (see QDomDocument). + QXmlStreamReader reads data either from a QIODevice (see + setDevice()), or from a raw QByteArray (see addData()). + + 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 well-formed XML 1.0 parser that does \e not + include external parsed entities. As long as no error occurs, the + application code can thus be assured that the data provided by the + stream reader satisfies the W3C's criteria for well-formed XML. For + example, you can be certain that all tags are indeed nested and + closed properly, that references to internal entities have been + replaced with the correct replacement text, and that attributes have + been normalized or added according to the internal subset of the + DTD. + + 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 QStringRef rather than real QString + objects. QStringRef is a thin wrapper around QString substrings that + provides a subset of the QString API without the memory allocation + and reference-counting overhead. Calling + \l{QStringRef::toString()}{toString()} on any of those objects + returns an equivalent real QString object. + +*/ + + +/*! + 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); +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const QByteArray &data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); + d->dataBuffer = data; +} + +/*! + Creates a new stream reader that reads from \a data. + + This function should only be used if the XML header either says the encoding + is "UTF-8" or lacks any encoding information (the latter is the case of + QXmlStreamWriter writing to a QString). Any other encoding is likely going to + cause data corruption ("mojibake"). + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const QString &data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); +#ifdef QT_NO_TEXTCODEC + d->dataBuffer = data.toLatin1(); +#else + d->dataBuffer = d->codec->fromUnicode(data); + d->decoder = d->codec->makeDecoder(); +#endif + d->lockEncoding = true; + +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const char *data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); + d->dataBuffer = QByteArray(data); +} + +/*! + 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 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QXmlStreamReader::device() const +{ + Q_D(const QXmlStreamReader); + return d->device; +} + + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void QXmlStreamReader::addData(const QByteArray &data) +{ + Q_D(QXmlStreamReader); + if (d->device) { + qWarning("QXmlStreamReader: addData() with device()"); + return; + } + d->dataBuffer += data; +} + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void QXmlStreamReader::addData(const QString &data) +{ + Q_D(QXmlStreamReader); + d->lockEncoding = true; +#ifdef QT_NO_TEXTCODEC + addData(data.toLatin1()); +#else + addData(d->codec->fromUnicode(data)); +#endif +} + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void QXmlStreamReader::addData(const char *data) +{ + addData(QByteArray(data)); +} + +/*! + 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 = 0; + } +} + +/*! + 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->dataBuffer.size(); + } + 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(); + } + 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()) + 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; + } +} + +/* + * Use the following Perl script to generate the error string index list: +===== PERL SCRIPT ==== +print "static const char QXmlStreamReader_tokenTypeString_string[] =\n"; +$counter = 0; +$i = 0; +while (<STDIN>) { + chomp; + print " \"$_\\0\"\n"; + $sizes[$i++] = $counter; + $counter += length 1 + $_; +} +print " \"\\0\";\n\nstatic const short QXmlStreamReader_tokenTypeString_indices[] = {\n "; +for ($j = 0; $j < $i; ++$j) { + printf "$sizes[$j], "; +} +print "0\n};\n"; +===== PERL SCRIPT ==== + + * The input data is as follows (copied from qxmlstream.h): +NoToken +Invalid +StartDocument +EndDocument +StartElement +EndElement +Characters +Comment +DTD +EntityReference +ProcessingInstruction +*/ +static const char QXmlStreamReader_tokenTypeString_string[] = + "NoToken\0" + "Invalid\0" + "StartDocument\0" + "EndDocument\0" + "StartElement\0" + "EndElement\0" + "Characters\0" + "Comment\0" + "DTD\0" + "EntityReference\0" + "ProcessingInstruction\0"; + +static const short QXmlStreamReader_tokenTypeString_indices[] = { + 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 +}; + + +/*! + \property QXmlStreamReader::namespaceProcessing + 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 QLatin1String(QXmlStreamReader_tokenTypeString_string + + QXmlStreamReader_tokenTypeString_indices[d->type]); +} + +#endif // QT_NO_XMLSTREAMREADER + +QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() +{ + tagStack.reserve(16); + tagStackStringStorage.reserve(32); + tagStackStringStorageSize = 0; + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix = addToStringStorage(QStringViewLiteral("xml")); + namespaceDeclaration.namespaceUri = addToStringStorage(QStringViewLiteral("http://www.w3.org/XML/1998/namespace")); + initialTagStackStringStorageSize = tagStackStringStorageSize; +} + +#ifndef QT_NO_XMLSTREAMREADER + +QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q) + :q_ptr(q) +{ + device = 0; + deleteDevice = false; +#ifndef QT_NO_TEXTCODEC + decoder = 0; +#endif + stack_size = 64; + sym_stack = 0; + state_stack = 0; + reallocateStack(); + entityResolver = 0; + init(); +#define ADD_PREDEFINED(n, v) \ + do { \ + Entity e = Entity::createLiteral(QLatin1String(n), QLatin1String(v)); \ + 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 +} + +void QXmlStreamReaderPrivate::init() +{ + scanDtd = false; + token = -1; + token_char = 0; + isEmptyElement = false; + isWhitespace = true; + isCDATA = false; + standalone = false; + tos = 0; + resumeReduction = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + putStack.clear(); + putStack.reserve(32); + textBuffer.clear(); + textBuffer.reserve(256); + tagStack.clear(); + tagsDone = false; + attributes.clear(); + attributes.reserve(16); + lineNumber = lastLineStart = characterOffset = 0; + readBufferPos = 0; + nbytesread = 0; +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForMib(106); // utf8 + delete decoder; + decoder = 0; +#endif + attributeStack.clear(); + attributeStack.reserve(16); + entityParser = 0; + hasCheckedStartDocument = false; + normalizeLiterals = false; + hasSeenTag = false; + atEnd = false; + inParseEntity = false; + referenceToUnparsedEntityDetected = false; + referenceToParameterEntityDetected = false; + hasExternalDtdSubset = false; + lockEncoding = false; + namespaceProcessing = true; + rawReadBuffer.clear(); + dataBuffer.clear(); + readBuffer.clear(); + tagStackStringStorageSize = initialTagStackStringStorageSize; + + type = QXmlStreamReader::NoToken; + error = QXmlStreamReader::NoError; +} + +/* + 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 = new 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() +{ + stack_size <<= 1; + sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); + Q_CHECK_PTR(sym_stack); + state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); + Q_CHECK_PTR(state_stack); +} + + +QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate() +{ +#ifndef QT_NO_TEXTCODEC + delete decoder; +#endif + free(sym_stack); + free(state_stack); + delete entityParser; +} + + +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) +{ + int pos = textBuffer.size(); + int 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) +{ + int 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; + } + for (int i = 0; i < n; ++i) + textBuffer += QChar(ushort(str[i])); + if (requireSpace) { + int s = fastScanSpace(); + if (!s || atEnd) { + int 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 int QXmlStreamReaderPrivate::fastScanLiteralContent() +{ + int 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 += QLatin1Char(' '); + 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(c); + ++n; + } + } + return n; +} + +inline int QXmlStreamReaderPrivate::fastScanSpace() +{ + int 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 int QXmlStreamReaderPrivate::fastScanContentCharList() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + switch (ushort(c)) { + case 0xfffe: + case 0xffff: + case 0: + putChar(c); + return n; + case ']': { + isWhitespace = false; + int pos = textBuffer.size(); + textBuffer += QChar(ushort(c)); + ++n; + while ((c = getChar()) == ']') { + textBuffer += QChar(ushort(c)); + ++n; + } + if (c == 0) { + putString(textBuffer, pos); + textBuffer.resize(pos); + } else if (c == '>' && textBuffer.at(textBuffer.size()-2) == QLatin1Char(']')) { + 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; +} + +inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + 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 (prefix && *prefix == n+1) { + *prefix = 0; + putChar(':'); + --n; + } + return n; + case ':': + if (prefix) { + if (*prefix == 0) { + *prefix = 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(c); + ++n; + } + } + + if (prefix) + *prefix = 0; + int 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 int QXmlStreamReaderPrivate::fastScanNMTOKEN() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + if (fastDetermineNameChar(c) == NotName) { + putChar(c); + return n; + } else { + ++n; + textBuffer += QChar(c); + } + } + + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); + + return n; +} + +void QXmlStreamReaderPrivate::putString(const QString &s, int from) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= from; --i) + putStack.rawPush() = s.at(i).unicode(); +} + +void QXmlStreamReaderPrivate::putStringLiteral(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) + putStack.rawPush() = ((LETTER << 16) | s.at(i).unicode()); +} + +void QXmlStreamReaderPrivate::putReplacement(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) { + ushort c = s.at(i).unicode(); + if (c == '\n' || c == '\r') + putStack.rawPush() = ((LETTER << 16) | c); + else + putStack.rawPush() = c; + } +} +void QXmlStreamReaderPrivate::putReplacementInAttributeValue(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) { + ushort c = s.at(i).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() +{ + const int BUFFER_SIZE = 8192; + characterOffset += readBufferPos; + readBufferPos = 0; + readBuffer.resize(0); +#ifndef QT_NO_TEXTCODEC + if (decoder) +#endif + nbytesread = 0; + if (device) { + rawReadBuffer.resize(BUFFER_SIZE); + int nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread); + nbytesread += qMax(nbytesreadOrMinus1, 0); + } else { + if (nbytesread) + rawReadBuffer += dataBuffer; + else + rawReadBuffer = dataBuffer; + nbytesread = rawReadBuffer.size(); + dataBuffer.clear(); + } + if (!nbytesread) { + atEnd = true; + return StreamEOF; + } + +#ifndef QT_NO_TEXTCODEC + if (!decoder) { + if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus + // one extra for the utf8 codec + atEnd = true; + return StreamEOF; + } + int mib = 106; // UTF-8 + + // look for byte order mark + uchar ch1 = rawReadBuffer.at(0); + uchar ch2 = rawReadBuffer.at(1); + uchar ch3 = rawReadBuffer.at(2); + uchar ch4 = rawReadBuffer.at(3); + + if ((ch1 == 0 && ch2 == 0 && ch3 == 0xfe && ch4 == 0xff) || + (ch1 == 0xff && ch2 == 0xfe && ch3 == 0 && ch4 == 0)) + mib = 1017; // UTF-32 with byte order mark + else if (ch1 == 0x3c && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x00) + mib = 1019; // UTF-32LE + else if (ch1 == 0x00 && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x3c) + mib = 1018; // UTF-32BE + else if ((ch1 == 0xfe && ch2 == 0xff) || (ch1 == 0xff && ch2 == 0xfe)) + mib = 1015; // UTF-16 with byte order mark + else if (ch1 == 0x3c && ch2 == 0x00) + mib = 1014; // UTF-16LE + else if (ch1 == 0x00 && ch2 == 0x3c) + mib = 1013; // UTF-16BE + codec = QTextCodec::codecForMib(mib); + Q_ASSERT(codec); + decoder = codec->makeDecoder(); + } + + decoder->toUnicode(&readBuffer, rawReadBuffer.constData(), nbytesread); + + if(lockEncoding && decoder->hasFailure()) { + raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return StreamEOF; + } +#else + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#endif // QT_NO_TEXTCODEC + + 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; +} + +QStringRef QXmlStreamReaderPrivate::namespaceForPrefix(const QStringRef &prefix) +{ + for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { + const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(j); + 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 QStringRef(); +} + +/* + uses namespaceForPrefix and builds the attribute vector + */ +void QXmlStreamReaderPrivate::resolveTag() +{ + int n = attributeStack.size(); + + if (namespaceProcessing) { + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isNamespaceAttribute + || dtdAttribute.defaultValue.isNull() + || dtdAttribute.tagName != qualifiedName + || dtdAttribute.attributeQualifiedName.isNull()) + continue; + int i = 0; + while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) + ++i; + if (i != n) + continue; + if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == QLatin1String("xmlns")) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(dtdAttribute.defaultValue); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = ns; + } else if (dtdAttribute.attributePrefix == QLatin1String("xmlns")) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = dtdAttribute.attributeName; + QStringRef namespaceUri = dtdAttribute.defaultValue; + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = namespacePrefix; + namespaceDeclaration.namespaceUri = namespaceUri; + } + } + } + + tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix); + + attributes.resize(n); + + for (int i = 0; i < n; ++i) { + QXmlStreamAttribute &attribute = attributes[i]; + Attribute &attrib = attributeStack[i]; + QStringRef prefix(symPrefix(attrib.key)); + QStringRef name(symString(attrib.key)); + QStringRef qualifiedName(symName(attrib.key)); + QStringRef value(symString(attrib.value)); + + attribute.m_name = QXmlStreamStringRef(name); + attribute.m_qualifiedName = QXmlStreamStringRef(qualifiedName); + attribute.m_value = QXmlStreamStringRef(value); + + if (!prefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(prefix); + attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); + } + + for (int j = 0; j < i; ++j) { + if (attributes[j].name() == attribute.name() + && attributes[j].namespaceUri() == attribute.namespaceUri() + && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName())) + raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName())); + } + } + + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (dtdAttribute.isNamespaceAttribute + || dtdAttribute.defaultValue.isNull() + || dtdAttribute.tagName != qualifiedName + || dtdAttribute.attributeQualifiedName.isNull()) + continue; + int i = 0; + while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) + ++i; + if (i != n) + continue; + + + + QXmlStreamAttribute attribute; + attribute.m_name = QXmlStreamStringRef(dtdAttribute.attributeName); + attribute.m_qualifiedName = QXmlStreamStringRef(dtdAttribute.attributeQualifiedName); + attribute.m_value = QXmlStreamStringRef(dtdAttribute.defaultValue); + + if (!dtdAttribute.attributePrefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix); + attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); + } + attribute.m_isDefault = true; + attributes.append(attribute); + } + + attributeStack.clear(); +} + +void QXmlStreamReaderPrivate::resolvePublicNamespaces() +{ + const Tag &tag = tagStack.top(); + int n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize; + publicNamespaceDeclarations.resize(n); + for (int i = 0; i < n; ++i) { + const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i); + QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i]; + publicNamespaceDeclaration.m_prefix = QXmlStreamStringRef(namespaceDeclaration.prefix); + publicNamespaceDeclaration.m_namespaceUri = QXmlStreamStringRef(namespaceDeclaration.namespaceUri); + } +} + +void QXmlStreamReaderPrivate::resolveDtd() +{ + publicNotationDeclarations.resize(notationDeclarations.size()); + for (int i = 0; i < notationDeclarations.size(); ++i) { + const QXmlStreamReaderPrivate::NotationDeclaration ¬ationDeclaration = notationDeclarations.at(i); + QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i]; + publicNotationDeclaration.m_name = QXmlStreamStringRef(notationDeclaration.name); + publicNotationDeclaration.m_systemId = QXmlStreamStringRef(notationDeclaration.systemId); + publicNotationDeclaration.m_publicId = QXmlStreamStringRef(notationDeclaration.publicId); + + } + notationDeclarations.clear(); + publicEntityDeclarations.resize(entityDeclarations.size()); + for (int i = 0; i < entityDeclarations.size(); ++i) { + const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i); + QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i]; + publicEntityDeclaration.m_name = QXmlStreamStringRef(entityDeclaration.name); + publicEntityDeclaration.m_notationName = QXmlStreamStringRef(entityDeclaration.notationName); + publicEntityDeclaration.m_systemId = QXmlStreamStringRef(entityDeclaration.systemId); + publicEntityDeclaration.m_publicId = QXmlStreamStringRef(entityDeclaration.publicId); + publicEntityDeclaration.m_value = QXmlStreamStringRef(entityDeclaration.value); + } + entityDeclarations.clear(); + parameterEntityHash.clear(); +} + +uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex) +{ + bool ok = true; + uint s; + // ### add toXShort to QStringRef? + if (sym(symbolIndex).c == 'x') + s = symString(symbolIndex, 1).toUInt(&ok, 16); + else + s = symString(symbolIndex).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(const QStringRef &publicId) +{ +//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] + + const ushort *data = reinterpret_cast<const ushort *>(publicId.constData()); + uchar c = 0; + int 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 ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9')) + 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 != QLatin1String("1.0")) { + if (documentVersion.contains(QLatin1Char(' '))) + err = QXmlStream::tr("Invalid XML version string."); + else + err = QXmlStream::tr("Unsupported XML version."); + } + int 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? '?>' */ + bool hasStandalone = false; + + for (int i = 0; err.isNull() && i < n; ++i) { + Attribute &attrib = attributeStack[i]; + QStringRef prefix(symPrefix(attrib.key)); + QStringRef key(symString(attrib.key)); + QStringRef value(symString(attrib.value)); + + if (prefix.isEmpty() && key == QLatin1String("encoding")) { + 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 { +#ifdef QT_NO_TEXTCODEC + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#else + QTextCodec *const newCodec = QTextCodec::codecForName(value.toLatin1()); + if (!newCodec) + err = QXmlStream::tr("Encoding %1 is unsupported").arg(value); + else if (newCodec != codec && !lockEncoding) { + codec = newCodec; + delete decoder; + decoder = codec->makeDecoder(); + decoder->toUnicode(&readBuffer, rawReadBuffer.data(), nbytesread); + } +#endif // QT_NO_TEXTCODEC + } + } else if (prefix.isEmpty() && key == QLatin1String("standalone")) { + hasStandalone = true; + if (value == QLatin1String("yes")) + standalone = true; + else if (value == QLatin1String("no")) + standalone = false; + else + err = QXmlStream::tr("Standalone accepts only yes or no."); + } else { + err = QXmlStream::tr("Invalid attribute in XML declaration."); + } + } + + 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::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 != 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(QLatin1String(spell[expected[0]])); + if (nexpected == 2) { + //: <first option>, '<second option>' + exp_str = QXmlStream::tr("%1 or '%2'", "expected").arg(exp_str, QLatin1String(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, QLatin1String(spell[expected[s]])); + } + //: <options so far>, or '<final option>' + exp_str = QXmlStream::tr("%1, or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); + } + error_message = QXmlStream::tr("Expected %1, but got '%2'.").arg(exp_str, QLatin1String(spell[token])); + } else { + error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1String(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. + */ +QStringRef 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 QVector + of QXmlStreamNotationDeclaration. + */ +QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->notationDeclarations.size()) + 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 QVector + of QXmlStreamEntityDeclaration. + */ +QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->entityDeclarations.size()) + 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. + + */ +QStringRef QXmlStreamReader::dtdName() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdName; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + public identifier. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdPublicId() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdPublicId; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + system identifier. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdSystemId() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdSystemId; + return QStringRef(); +} + +/*! 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 QVector + 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 (int i = 0; i < extraNamespaceDeclarations.size(); ++i) + addExtraNamespaceDeclaration(extraNamespaceDeclarations.at(i)); +} + + +/*! 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; + forever { + switch (readNext()) { + case Characters: + case EntityReference: + result.insert(result.size(), d->text.unicode(), d->text.size()); + break; + case EndElement: + return result; + case ProcessingInstruction: + case Comment: + break; + case StartElement: + if (behaviour == SkipChildElements) { + skipCurrentElement(); + break; + } else if (behaviour == IncludeChildElements) { + result += readElementText(behaviour); + break; + } + Q_FALLTHROUGH(); + default: + if (d->error || behaviour == ErrorOnUnexpectedElement) { + if (!d->error) + d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data.")); + 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. + */ +QStringRef QXmlStreamReader::processingInstructionTarget() const +{ + Q_D(const QXmlStreamReader); + return d->processingInstructionTarget; +} + +/*! + Returns the data of a ProcessingInstruction. + */ +QStringRef QXmlStreamReader::processingInstructionData() const +{ + Q_D(const QXmlStreamReader); + return d->processingInstructionData; +} + + + +/*! + Returns the local name of a StartElement, EndElement, or an EntityReference. + + \sa namespaceUri(), qualifiedName() + */ +QStringRef QXmlStreamReader::name() const +{ + Q_D(const QXmlStreamReader); + return d->name; +} + +/*! + Returns the namespaceUri of a StartElement or EndElement. + + \sa name(), qualifiedName() + */ +QStringRef 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() + */ +QStringRef QXmlStreamReader::qualifiedName() const +{ + Q_D(const QXmlStreamReader); + return d->qualifiedName; +} + + + +/*! + \since 4.4 + + Returns the prefix of a StartElement or EndElement. + + \sa name(), qualifiedName() +*/ +QStringRef 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 // QT_NO_XMLSTREAMREADER + +/*! + \class QXmlStreamAttribute + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamAttribute class represents a single XML attribute + + \ingroup xml-tools + + 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; +} + +/*! + Destructs an attribute. + */ +QXmlStreamAttribute::~QXmlStreamAttribute() +{ +} + +/*! Constructs an attribute in the namespace described with \a + namespaceUri with \a name and value \a value. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); + m_name = m_qualifiedName = QXmlStreamStringRef(QStringRef(&name)); + m_value = QXmlStreamStringRef(QStringRef(&value)); + m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); +} + +/*! + Constructs an attribute with qualified name \a qualifiedName and value \a value. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value) +{ + int colon = qualifiedName.indexOf(QLatin1Char(':')); + m_name = QXmlStreamStringRef(QStringRef(&qualifiedName, + colon + 1, + qualifiedName.size() - (colon + 1))); + m_qualifiedName = QXmlStreamStringRef(QStringRef(&qualifiedName)); + m_value = QXmlStreamStringRef(QStringRef(&value)); +} + +/*! \fn QStringRef QXmlStreamAttribute::namespaceUri() const + + Returns the attribute's resolved namespaceUri, or an empty string + reference if the attribute does not have a defined namespace. + */ +/*! \fn QStringRef QXmlStreamAttribute::name() const + Returns the attribute's local name. + */ +/*! \fn QStringRef 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 QStringRef QXmlStreamAttribute::prefix() const + \since 4.4 + Returns the attribute's namespace prefix. + + \sa name(), qualifiedName() + +*/ + +/*! \fn QStringRef 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 &other) const + + Compares this attribute with \a other and returns \c true if they are + equal; otherwise returns \c false. + */ +/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other) const + + Compares this attribute with \a other and returns \c true if they are + not equal; otherwise returns \c false. + */ + + +/*! + Creates a copy of \a other. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QXmlStreamAttribute &other) +{ + *this = other; +} + +/*! + Assigns \a other to this attribute. + */ +QXmlStreamAttribute& QXmlStreamAttribute::operator=(const QXmlStreamAttribute &other) +{ + m_name = other.m_name; + m_namespaceUri = other.m_namespaceUri; + m_qualifiedName = other.m_qualifiedName; + m_value = other.m_value; + m_isDefault = other.m_isDefault; + return *this; +} + + +/*! + \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 QVector<QXmlStreamNotationDeclaration>. +*/ + + +/*! + \class QXmlStreamNotationDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration. + + \ingroup xml-tools + + An notation declaration consists of a name(), a systemId(), and a publicId(). +*/ + +/*! + Creates an empty notation declaration. +*/ +QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration() +{ +} +/*! + Creates a copy of \a other. + */ +QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this notation declaration. + */ +QXmlStreamNotationDeclaration& QXmlStreamNotationDeclaration::operator=(const QXmlStreamNotationDeclaration &other) +{ + m_name = other.m_name; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + return *this; +} + +/*! +Destructs this notation declaration. +*/ +QXmlStreamNotationDeclaration::~QXmlStreamNotationDeclaration() +{ +} + +/*! \fn QStringRef QXmlStreamNotationDeclaration::name() const + +Returns the notation name. +*/ +/*! \fn QStringRef QXmlStreamNotationDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef QXmlStreamNotationDeclaration::publicId() const + +Returns the public identifier. +*/ + +/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns \c true + if they are equal; otherwise returns \c false. + */ +/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns \c true + if they are not equal; otherwise returns \c false. + */ + +/*! + \typedef QXmlStreamNamespaceDeclarations + \relates QXmlStreamNamespaceDeclaration + + Synonym for QVector<QXmlStreamNamespaceDeclaration>. +*/ + +/*! + \class QXmlStreamNamespaceDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration. + + \ingroup xml-tools + + An namespace declaration consists of a prefix() and a namespaceUri(). +*/ +/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns \c true + if they are equal; otherwise returns \c false. + */ +/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other 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; +} + +/*! + Creates a copy of \a other. + */ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this namespace declaration. + */ +QXmlStreamNamespaceDeclaration& QXmlStreamNamespaceDeclaration::operator=(const QXmlStreamNamespaceDeclaration &other) +{ + m_prefix = other.m_prefix; + m_namespaceUri = other.m_namespaceUri; + return *this; +} +/*! +Destructs this namespace declaration. +*/ +QXmlStreamNamespaceDeclaration::~QXmlStreamNamespaceDeclaration() +{ +} + +/*! \fn QStringRef QXmlStreamNamespaceDeclaration::prefix() const + +Returns the prefix. +*/ +/*! \fn QStringRef QXmlStreamNamespaceDeclaration::namespaceUri() const + +Returns the namespaceUri. +*/ + + + + +/*! + \typedef QXmlStreamEntityDeclarations + \relates QXmlStreamEntityDeclaration + + Synonym for QVector<QXmlStreamEntityDeclaration>. +*/ + +/*! + \class QXmlStreamStringRef + \inmodule QtCore + \since 4.3 + \internal +*/ + +/*! + \class QXmlStreamEntityDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration. + + \ingroup xml-tools + + An entity declaration consists of a name(), a notationName(), a + systemId(), a publicId(), and a value(). +*/ + +/*! + Creates an empty entity declaration. +*/ +QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration() +{ +} + +/*! + Creates a copy of \a other. + */ +QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this entity declaration. + */ +QXmlStreamEntityDeclaration& QXmlStreamEntityDeclaration::operator=(const QXmlStreamEntityDeclaration &other) +{ + m_name = other.m_name; + m_notationName = other.m_notationName; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + m_value = other.m_value; + return *this; +} + +/*! + Destructs this entity declaration. +*/ +QXmlStreamEntityDeclaration::~QXmlStreamEntityDeclaration() +{ +} + +/*! \fn QXmlStreamStringRef::swap(QXmlStreamStringRef &other) + \since 5.6 + + Swaps this string reference's contents with \a other. + This function is very fast and never fails. +*/ + +/*! \fn QStringRef QXmlStreamEntityDeclaration::name() const + +Returns the entity name. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::notationName() const + +Returns the notation name. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::publicId() const + +Returns the public identifier. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::value() const + +Returns the entity's value. +*/ + +/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns \c true if + they are equal; otherwise returns \c false. + */ +/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other 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. + */ +QStringRef QXmlStreamAttributes::value(const QString &namespaceUri, const QString &name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + 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. + */ +QStringRef QXmlStreamAttributes::value(const QString &namespaceUri, QLatin1String name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + 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. + */ +QStringRef QXmlStreamAttributes::value(QLatin1String namespaceUri, QLatin1String name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\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. + */ +QStringRef QXmlStreamAttributes::value(const QString &qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.qualifiedName() == qualifiedName) + return attribute.value(); + } + return QStringRef(); +} + +/*!\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. + */ +QStringRef QXmlStreamAttributes::value(QLatin1String qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.qualifiedName() == qualifiedName) + return attribute.value(); + } + return QStringRef(); +} + +/*!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)); +} + +#ifndef QT_NO_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. + */ +bool QXmlStreamReader::isStandaloneDocument() const +{ + Q_D(const QXmlStreamReader); + return d->standalone; +} + + +/*! + \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. + */ +QStringRef QXmlStreamReader::documentVersion() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::StartDocument) + return d->documentVersion; + return QStringRef(); +} + +/*! + \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. + */ +QStringRef QXmlStreamReader::documentEncoding() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::StartDocument) + return d->documentEncoding; + return QStringRef(); +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class QXmlStreamWriter + \inmodule QtCore + \since 4.3 + \reentrant + + \brief The QXmlStreamWriter class provides an XML writer with a + simple streaming API. + + \ingroup xml-tools + + QXmlStreamWriter is the counterpart to QXmlStreamReader for writing + XML. 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(). + + By default, QXmlStreamWriter encodes XML in UTF-8. Different + encodings can be enforced using setCodec(). + + If an error occurs while writing to the underlying device, hasError() + starts returning true and subsequent writes are ignored. + + 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. + +*/ + +#ifndef QT_NO_XMLSTREAMWRITER + +class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack { + QXmlStreamWriter *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamWriter) +public: + QXmlStreamWriterPrivate(QXmlStreamWriter *q); + ~QXmlStreamWriterPrivate() { + if (deleteDevice) + delete device; +#ifndef QT_NO_TEXTCODEC + delete encoder; +#endif + } + + void write(const QStringRef &); + void write(const QString &); + void writeEscaped(const QString &, bool escapeWhitespace = false); + void write(const char *s, int len); + template <int N> void write(const char (&s)[N]) { write(s, N - 1); } + bool finishStartElement(bool contents = true); + void writeStartElement(const QString &namespaceUri, const QString &name); + QIODevice *device; + QString *stringDevice; + uint deleteDevice :1; + uint inStartElement :1; + uint inEmptyElement :1; + uint lastWasStartElement :1; + uint wroteSomething :1; + uint hasIoError :1; + uint hasEncodingError :1; + uint autoFormatting :1; + uint isCodecASCIICompatible :1; + QByteArray autoFormattingIndent; + NamespaceDeclaration emptyNamespace; + int lastNamespaceDeclaration; + +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextEncoder *encoder; +#endif + void checkIfASCIICompatibleCodec(); + + NamespaceDeclaration &findNamespace(const QString &namespaceUri, bool writeDeclaration = false, bool noDefault = false); + void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration); + + int namespacePrefixCount; + + void indent(int level); +}; + + +QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) + :autoFormattingIndent(4, ' ') +{ + q_ptr = q; + device = 0; + stringDevice = 0; + deleteDevice = false; +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForMib(106); // utf8 + encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 +#endif + checkIfASCIICompatibleCodec(); + inStartElement = inEmptyElement = false; + wroteSomething = false; + hasIoError = false; + hasEncodingError = false; + lastWasStartElement = false; + lastNamespaceDeclaration = 1; + autoFormatting = false; + namespacePrefixCount = 0; +} + +void QXmlStreamWriterPrivate::checkIfASCIICompatibleCodec() +{ +#ifndef QT_NO_TEXTCODEC + Q_ASSERT(encoder); + // test ASCII-compatibility using the letter 'a' + QChar letterA = QLatin1Char('a'); + const QByteArray bytesA = encoder->fromUnicode(&letterA, 1); + const bool isCodecASCIICompatibleA = (bytesA.count() == 1) && (bytesA[0] == 0x61) ; + QChar letterLess = QLatin1Char('<'); + const QByteArray bytesLess = encoder->fromUnicode(&letterLess, 1); + const bool isCodecASCIICompatibleLess = (bytesLess.count() == 1) && (bytesLess[0] == 0x3C) ; + isCodecASCIICompatible = isCodecASCIICompatibleA && isCodecASCIICompatibleLess ; +#else + isCodecASCIICompatible = true; +#endif +} + +void QXmlStreamWriterPrivate::write(const QStringRef &s) +{ + if (device) { + if (hasIoError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); + if (encoder->hasFailure()) { + hasEncodingError = true; + return; + } +#endif + if (device->write(bytes) != bytes.size()) + hasIoError = true; + } + else if (stringDevice) + s.appendTo(stringDevice); + else + qWarning("QXmlStreamWriter: No device"); +} + +void QXmlStreamWriterPrivate::write(const QString &s) +{ + if (device) { + if (hasIoError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s); + if (encoder->hasFailure()) { + hasEncodingError = true; + return; + } +#endif + if (device->write(bytes) != bytes.size()) + hasIoError = true; + } + else if (stringDevice) + stringDevice->append(s); + else + qWarning("QXmlStreamWriter: No device"); +} + +void QXmlStreamWriterPrivate::writeEscaped(const QString &s, bool escapeWhitespace) +{ + QString escaped; + escaped.reserve(s.size()); + for ( int i = 0; i < s.size(); ++i ) { + QChar c = s.at(i); + switch (c.unicode()) { + case '<': + escaped.append(QLatin1String("<")); + break; + case '>': + escaped.append(QLatin1String(">")); + break; + case '&': + escaped.append(QLatin1String("&")); + break; + case '\"': + escaped.append(QLatin1String(""")); + break; + case '\t': + if (escapeWhitespace) + escaped.append(QLatin1String("	")); + else + escaped += c; + break; + case '\n': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + case '\v': + case '\f': + hasEncodingError = true; + break; + case '\r': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + default: + if (c.unicode() > 0x1f && c.unicode() < 0xfffe) + escaped += c; + else + hasEncodingError = true; + break; + } + } + write(escaped); +} + +// Converts from ASCII to output encoding +void QXmlStreamWriterPrivate::write(const char *s, int len) +{ + if (device) { + if (hasIoError) + return; + if (isCodecASCIICompatible) { + if (device->write(s, len) != len) + hasIoError = true; + return; + } + } + + write(QString::fromLatin1(s, len)); +} + +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("\""); + } +} + +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(); + return hadSomethingWritten; +} + +QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(const QString &namespaceUri, bool writeDeclaration, bool noDefault) +{ + for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations[j]; + 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 = QLatin1Char('n') + QString::number(n++); + int 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) +{ + write("\n"); + for (int i = level; i > 0; --i) + write(autoFormattingIndent.constData(), autoFormattingIndent.length()); +} + + +/*! + 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. + * + * Note that when writing to QString, QXmlStreamWriter ignores the codec set + * with setCodec(). See that function for more information. + */ +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 = 0; + if (d->deleteDevice) { + delete d->device; + d->deleteDevice = false; + } + d->device = device; +} + +/*! + Returns the current device associated with the QXmlStreamWriter, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QXmlStreamWriter::device() const +{ + Q_D(const QXmlStreamWriter); + return d->device; +} + + +#ifndef QT_NO_TEXTCODEC +/*! + Sets the codec for this stream to \a codec. The codec is used for + encoding any data that is written. By default, QXmlStreamWriter + uses UTF-8. + + The encoding information is stored in the initial xml tag which + gets written when you call writeStartDocument(). Call this + function before calling writeStartDocument(). + + \note When writing the XML to a QString, the codec information is ignored + and the XML header will not include any encoding information, since all + QStrings are UTF-16. If you later convert the QString to an 8-bit format, + you must arrange for the encoding information to be transmitted + out-of-band. + + \sa codec() +*/ +void QXmlStreamWriter::setCodec(QTextCodec *codec) +{ + Q_D(QXmlStreamWriter); + if (codec) { + d->codec = codec; + delete d->encoder; + d->encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 + d->checkIfASCIICompatibleCodec(); + } +} + +/*! + Sets the codec for this stream to the QTextCodec for the encoding + specified by \a codecName. Common values for \c codecName include + "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't + recognized, nothing happens. + + \note When writing the XML to a QString, the codec information is ignored + and the XML header will not include any encoding information, since all + QStrings are UTF-16. If you later convert the QString to an 8-bit format, + you must arrange for the encoding information to be transmitted + out-of-band. + + \sa QTextCodec::codecForName() +*/ +void QXmlStreamWriter::setCodec(const char *codecName) +{ + setCodec(QTextCodec::codecForName(codecName)); +} + +/*! + Returns the codec that is currently assigned to the stream. + + \sa setCodec() +*/ +QTextCodec *QXmlStreamWriter::codec() const +{ + Q_D(const QXmlStreamWriter); + return d->codec; +} +#endif // QT_NO_TEXTCODEC + +/*! + \property QXmlStreamWriter::autoFormatting + \since 4.4 + 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 formattting 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 = QByteArray(qAbs(spacesOrTabs), spacesOrTabs >= 0 ? ' ' : '\t'); +} + +int QXmlStreamWriter::autoFormattingIndent() const +{ + Q_D(const QXmlStreamWriter); + return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); +} + +/*! + Returns \c true if writing failed. + + This can happen if the stream failed to write to the underlying + device or if the data to be written contained invalid characters. + + The error status is never reset. Writes happening after the error + occurred may be ignored, even if the error condition is cleared. + */ +bool QXmlStreamWriter::hasError() const +{ + Q_D(const QXmlStreamWriter); + return d->hasIoError || d->hasEncodingError; +} + +/*! + \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(). + */ +void QXmlStreamWriter::writeAttribute(const QString &qualifiedName, const QString &value) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->write(" "); + d->write(qualifiedName); + d->write("=\""); + d->writeEscaped(value, true); + d->write("\""); +} + +/*! 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(). + */ +void QXmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + 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("\""); +} + +/*! + \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().toString(), + attribute.value().toString()); + else + writeAttribute(attribute.namespaceUri().toString(), + attribute.name().toString(), + attribute.value().toString()); +} + + +/*! 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 (int i = 0; i < attributes.size(); ++i) + writeAttribute(attributes.at(i)); +} + + +/*! 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. + */ +void QXmlStreamWriter::writeCDATA(const QString &text) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + QString copy(text); + copy.replace(QLatin1String("]]>"), QLatin1String("]]]]><![CDATA[>")); + d->write("<![CDATA["); + d->write(copy); + 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() + */ +void QXmlStreamWriter::writeCharacters(const QString &text) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + d->writeEscaped(text); +} + + +/*! Writes \a text as XML comment, where \a text must not contain the + forbidden sequence "--" or end with "-". Note that XML does not + provide any way to escape "-" in a comment. + */ +void QXmlStreamWriter::writeComment(const QString &text) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!text.contains(QLatin1String("--")) && !text.endsWith(QLatin1Char('-'))); + 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. + */ +void QXmlStreamWriter::writeDTD(const QString &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. +*/ +void QXmlStreamWriter::writeEmptyElement(const QString &qualifiedName) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->writeStartElement(QString(), 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() + */ +void QXmlStreamWriter::writeEmptyElement(const QString &namespaceUri, const QString &name) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + 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 + +*/ +void QXmlStreamWriter::writeTextElement(const QString &qualifiedName, const QString &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 + +*/ +void QXmlStreamWriter::writeTextElement(const QString &namespaceUri, const QString &name, const QString &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(); + d->write("\n"); +} + +/*! + Closes the previous start element. + + \sa writeStartElement() + */ +void QXmlStreamWriter::writeEndElement() +{ + Q_D(QXmlStreamWriter); + 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};". + */ +void QXmlStreamWriter::writeEntityReference(const QString &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. + + */ +void QXmlStreamWriter::writeNamespace(const QString &namespaceUri, const QString &prefix) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(prefix != QLatin1String("xmlns")); + if (prefix.isEmpty()) { + d->findNamespace(namespaceUri, d->inStartElement); + } else { + Q_ASSERT(!((prefix == QLatin1String("xml")) ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace")))); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix = d->addToStringStorage(prefix); + namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); + 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. + */ +void QXmlStreamWriter::writeDefaultNamespace(const QString &namespaceUri) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/XML/1998/namespace")); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + 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 "?>". + */ +void QXmlStreamWriter::writeProcessingInstruction(const QString &target, const QString &data) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!data.contains(QLatin1String("?>"))); + 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("?>"); +} + + + +/*!\overload + + Writes a document start with XML version number "1.0". This also + writes the encoding information. + + \sa writeEndDocument(), setCodec() + \since 4.5 + */ +void QXmlStreamWriter::writeStartDocument() +{ + writeStartDocument(QLatin1String("1.0")); +} + + +/*! + Writes a document start with the XML version number \a version. + + \sa writeEndDocument() + */ +void QXmlStreamWriter::writeStartDocument(const QString &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=\""); +#ifdef QT_NO_TEXTCODEC + d->write("iso-8859-1"); +#else + const QByteArray name = d->codec->name(); + d->write(name.constData(), name.length()); +#endif + } + d->write("\"?>"); +} + +/*! Writes a document start with the XML version number \a version + and a standalone attribute \a standalone. + + \sa writeEndDocument() + \since 4.5 + */ +void QXmlStreamWriter::writeStartDocument(const QString &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=\""); +#ifdef QT_NO_TEXTCODEC + d->write("iso-8859-1"); +#else + const QByteArray name = d->codec->name(); + d->write(name.constData(), name.length()); +#endif + } + if (standalone) + d->write("\" standalone=\"yes\"?>"); + else + d->write("\" standalone=\"no\"?>"); +} + + +/*!\overload + + Writes a start element with \a qualifiedName. Subsequent calls to + writeAttribute() will add attributes to this element. + + \sa writeEndElement(), writeEmptyElement() + */ +void QXmlStreamWriter::writeStartElement(const QString &qualifiedName) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->writeStartElement(QString(), 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() + */ +void QXmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + d->writeStartElement(namespaceUri, name); +} + +void QXmlStreamWriterPrivate::writeStartElement(const QString &namespaceUri, const QString &name) +{ + 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; + + for (int i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i) + writeNamespaceDeclaration(namespaceDeclarations[i]); + tag.namespaceDeclarationsSize = lastNamespaceDeclaration; +} + +#ifndef QT_NO_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) +{ + switch (reader.tokenType()) { + case QXmlStreamReader::NoToken: + break; + case QXmlStreamReader::StartDocument: + writeStartDocument(); + break; + case QXmlStreamReader::EndDocument: + writeEndDocument(); + break; + case QXmlStreamReader::StartElement: { + QXmlStreamNamespaceDeclarations namespaceDeclarations = reader.namespaceDeclarations(); + for (int i = 0; i < namespaceDeclarations.size(); ++i) { + const QXmlStreamNamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(i); + writeNamespace(namespaceDeclaration.namespaceUri().toString(), + namespaceDeclaration.prefix().toString()); + } + writeStartElement(reader.namespaceUri().toString(), reader.name().toString()); + writeAttributes(reader.attributes()); + } break; + case QXmlStreamReader::EndElement: + writeEndElement(); + break; + case QXmlStreamReader::Characters: + if (reader.isCDATA()) + writeCDATA(reader.text().toString()); + else + writeCharacters(reader.text().toString()); + break; + case QXmlStreamReader::Comment: + writeComment(reader.text().toString()); + break; + case QXmlStreamReader::DTD: + writeDTD(reader.text().toString()); + break; + case QXmlStreamReader::EntityReference: + writeEntityReference(reader.name().toString()); + break; + case QXmlStreamReader::ProcessingInstruction: + writeProcessingInstruction(reader.processingInstructionTarget().toString(), + reader.processingInstructionData().toString()); + break; + default: + Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid); + qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state."); + break; + } +} + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const + \since 4.5 + + 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(QLatin1String qualifiedName) const + \overload + \since 4.5 +*/ + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &namespaceUri, + const QString &name) const + \overload + \since 4.5 + + 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 // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMWRITER + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAM diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g new file mode 100644 index 0000000000..fd69a6e4af --- /dev/null +++ b/src/corelib/serialization/qxmlstream.g @@ -0,0 +1,1852 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 The Qt Company Ltd. +-- Contact: https://www.qt.io/licensing/ +-- +-- This file is part of the QtCore module of the Qt Toolkit. +-- +-- $QT_BEGIN_LICENSE:LGPL$ +-- Commercial License Usage +-- Licensees holding valid commercial Qt licenses may use this file in +-- accordance with the commercial license agreement provided with the +-- Software or, alternatively, in accordance with the terms contained in +-- a written agreement between you and The Qt Company. For licensing terms +-- and conditions see https://www.qt.io/terms-conditions. For further +-- information use the contact form at https://www.qt.io/contact-us. +-- +-- GNU Lesser General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU Lesser +-- General Public License version 3 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL3 included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 3 requirements +-- will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +-- +-- GNU General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU +-- General Public License version 2.0 or (at your option) the GNU General +-- Public license version 3 or any later version approved by the KDE Free +-- Qt Foundation. The licenses are as published by the Free Software +-- Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +-- included in the packaging of this file. Please review the following +-- information to ensure the GNU General Public License requirements will +-- be met: https://www.gnu.org/licenses/gpl-2.0.html and +-- https://www.gnu.org/licenses/gpl-3.0.html. +-- +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser QXmlStreamReader_Table + +%merged_output qxmlstream_p.h + +%token NOTOKEN +%token SPACE " " +%token LANGLE "<" +%token RANGLE ">" +%token AMPERSAND "&" +%token HASH "#" +%token QUOTE "\'" +%token DBLQUOTE "\"" +%token LBRACK "[" +%token RBRACK "]" +%token LPAREN "(" +%token RPAREN ")" +%token PIPE "|" +%token EQ "=" +%token PERCENT "%" +%token SLASH "/" +%token COLON ":" +%token SEMICOLON ";" +%token COMMA "," +%token DASH "-" +%token PLUS "+" +%token STAR "*" +%token DOT "." +%token QUESTIONMARK "?" +%token BANG "!" +%token LETTER "[a-zA-Z]" +%token DIGIT "[0-9]" + +-- after langle_bang +%token CDATA_START "[CDATA[" +%token DOCTYPE "DOCTYPE" +%token ELEMENT "ELEMENT" +%token ATTLIST "ATTLIST" +%token ENTITY "ENTITY" +%token NOTATION "NOTATION" + +-- entity decl +%token SYSTEM "SYSTEM" +%token PUBLIC "PUBLIC" +%token NDATA "NDATA" + +-- default decl +%token REQUIRED "REQUIRED" +%token IMPLIED "IMPLIED" +%token FIXED "FIXED" + +-- conent spec +%token EMPTY "EMPTY" +%token ANY "ANY" +%token PCDATA "PCDATA" + +-- error +%token ERROR + +-- entities +%token PARSE_ENTITY +%token ENTITY_DONE +%token UNRESOLVED_ENTITY + +-- att type +%token CDATA "CDATA" +%token ID "ID" +%token IDREF "IDREF" +%token IDREFS "IDREFS" +%token ENTITY "ENTITY" +%token ENTITIES "ENTITIES" +%token NMTOKEN "NMTOKEN" +%token NMTOKENS "NMTOKENS" + +-- xml declaration +%token XML "<?xml" +%token VERSION "version" + +%nonassoc SHIFT_THERE +%nonassoc AMPERSAND + BANG + COLON + COMMA + DASH + DBLQUOTE + DIGIT + DOT + ENTITY_DONE + EQ + HASH + LBRACK + LETTER + LPAREN + PERCENT + PIPE + PLUS + QUESTIONMARK + QUOTE + RANGLE + RBRACK + RPAREN + SEMICOLON + SLASH + SPACE + STAR + +%start document + +/. +template <typename T> class QXmlStreamSimpleStack { + T *data; + int tos, cap; +public: + inline QXmlStreamSimpleStack():data(0), tos(-1), cap(0){} + inline ~QXmlStreamSimpleStack(){ if (data) free(data); } + + inline void reserve(int extraCapacity) { + if (tos + extraCapacity + 1 > cap) { + cap = qMax(tos + extraCapacity + 1, cap << 1 ); + data = reinterpret_cast<T *>(realloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); + } + } + + inline T &push() { reserve(1); return data[++tos]; } + inline T &rawPush() { return data[++tos]; } + inline const T &top() const { return data[tos]; } + inline T &top() { return data[tos]; } + inline T &pop() { return data[tos--]; } + inline T &operator[](int index) { return data[index]; } + inline const T &at(int index) const { return data[index]; } + inline int size() const { return tos + 1; } + inline void resize(int s) { tos = s - 1; } + inline bool isEmpty() const { return tos < 0; } + inline void clear() { tos = -1; } +}; + + +class QXmlStream +{ + Q_DECLARE_TR_FUNCTIONS(QXmlStream) +}; + +class QXmlStreamPrivateTagStack { +public: + struct NamespaceDeclaration + { + QStringRef prefix; + QStringRef namespaceUri; + }; + + struct Tag + { + QStringRef name; + QStringRef qualifiedName; + NamespaceDeclaration namespaceDeclaration; + int tagStackStringStorageSize; + int namespaceDeclarationsSize; + }; + + + QXmlStreamPrivateTagStack(); + QXmlStreamSimpleStack<NamespaceDeclaration> namespaceDeclarations; + QString tagStackStringStorage; + int tagStackStringStorageSize; + int initialTagStackStringStorageSize; + bool tagsDone; + + inline QStringRef addToStringStorage(const QStringRef &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + inline QStringRef addToStringStorage(const QString &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + QStringRef addToStringStorage(QStringView s) + { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.append(s.data(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + + QXmlStreamSimpleStack<Tag> tagStack; + + + inline Tag &tagStack_pop() { + Tag& tag = tagStack.pop(); + tagStackStringStorageSize = tag.tagStackStringStorageSize; + namespaceDeclarations.resize(tag.namespaceDeclarationsSize); + tagsDone = tagStack.isEmpty(); + return tag; + } + inline Tag &tagStack_push() { + Tag &tag = tagStack.push(); + tag.tagStackStringStorageSize = tagStackStringStorageSize; + tag.namespaceDeclarationsSize = namespaceDeclarations.size(); + return tag; + } +}; + + +class QXmlStreamEntityResolver; +#ifndef QT_NO_XMLSTREAMREADER +class QXmlStreamReaderPrivate : public QXmlStreamReader_Table, public QXmlStreamPrivateTagStack{ + QXmlStreamReader *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamReader) +public: + QXmlStreamReaderPrivate(QXmlStreamReader *q); + ~QXmlStreamReaderPrivate(); + void init(); + + QByteArray rawReadBuffer; + QByteArray dataBuffer; + uchar firstByte; + qint64 nbytesread; + QString readBuffer; + int readBufferPos; + QXmlStreamSimpleStack<uint> putStack; + struct Entity { + Entity() = default; + Entity(const QString &name, const QString &value) + : name(name), value(value), external(false), unparsed(false), literal(false), + hasBeenParsed(false), isCurrentlyReferenced(false){} + static inline Entity createLiteral(QLatin1String name, QLatin1String value) + { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; } + QString name, value; + uint external : 1; + uint unparsed : 1; + uint literal : 1; + uint hasBeenParsed : 1; + uint isCurrentlyReferenced : 1; + }; + // these hash tables use a QStringView as a key to avoid creating QStrings + // just for lookup. The keys are usually views into Entity::name and thus + // are guaranteed to have the same lifetime as the referenced data: + QHash<QStringView, Entity> entityHash; + QHash<QStringView, Entity> parameterEntityHash; + QXmlStreamSimpleStack<Entity *>entityReferenceStack; + inline bool referenceEntity(Entity &entity) { + if (entity.isCurrentlyReferenced) { + raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + return false; + } + entity.isCurrentlyReferenced = true; + entityReferenceStack.push() = &entity; + injectToken(ENTITY_DONE); + return true; + } + + + QIODevice *device; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextDecoder *decoder; +#endif + bool atEnd; + + /*! + \sa setType() + */ + QXmlStreamReader::TokenType type; + QXmlStreamReader::Error error; + QString errorString; + QString unresolvedEntity; + + qint64 lineNumber, lastLineStart, characterOffset; + + + void write(const QString &); + void write(const char *); + + + QXmlStreamAttributes attributes; + QStringRef namespaceForPrefix(const QStringRef &prefix); + void resolveTag(); + void resolvePublicNamespaces(); + void resolveDtd(); + uint resolveCharRef(int symbolIndex); + bool checkStartDocument(); + void startDocument(); + void parseError(); + void checkPublicLiteral(const QStringRef &publicId); + + bool scanDtd; + QStringRef lastAttributeValue; + bool lastAttributeIsCData; + struct DtdAttribute { + QStringRef tagName; + QStringRef attributeQualifiedName; + QStringRef attributePrefix; + QStringRef attributeName; + QStringRef defaultValue; + bool isCDATA; + bool isNamespaceAttribute; + }; + QXmlStreamSimpleStack<DtdAttribute> dtdAttributes; + struct NotationDeclaration { + QStringRef name; + QStringRef publicId; + QStringRef systemId; + }; + QXmlStreamSimpleStack<NotationDeclaration> notationDeclarations; + QXmlStreamNotationDeclarations publicNotationDeclarations; + QXmlStreamNamespaceDeclarations publicNamespaceDeclarations; + + struct EntityDeclaration { + QStringRef name; + QStringRef notationName; + QStringRef publicId; + QStringRef systemId; + QStringRef value; + bool parameter; + bool external; + inline void clear() { + name.clear(); + notationName.clear(); + publicId.clear(); + systemId.clear(); + value.clear(); + parameter = external = false; + } + }; + QXmlStreamSimpleStack<EntityDeclaration> entityDeclarations; + QXmlStreamEntityDeclarations publicEntityDeclarations; + + QStringRef text; + + QStringRef prefix, namespaceUri, qualifiedName, name; + QStringRef processingInstructionTarget, processingInstructionData; + QStringRef dtdName, dtdPublicId, dtdSystemId; + QStringRef documentVersion, documentEncoding; + uint isEmptyElement : 1; + uint isWhitespace : 1; + uint isCDATA : 1; + uint standalone : 1; + uint hasCheckedStartDocument : 1; + uint normalizeLiterals : 1; + uint hasSeenTag : 1; + uint inParseEntity : 1; + uint referenceToUnparsedEntityDetected : 1; + uint referenceToParameterEntityDetected : 1; + uint hasExternalDtdSubset : 1; + uint lockEncoding : 1; + uint namespaceProcessing : 1; + + int resumeReduction; + void resume(int rule); + + inline bool entitiesMustBeDeclared() const { + return (!inParseEntity + && (standalone + || (!referenceToUnparsedEntityDetected + && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 + && !hasExternalDtdSubset))); + } + + // qlalr parser + int tos; + int stack_size; + struct Value { + int pos; + int len; + int prefix; + ushort c; + }; + + Value *sym_stack; + int *state_stack; + inline void reallocateStack(); + inline Value &sym(int index) const + { return sym_stack[tos + index - 1]; } + QString textBuffer; + inline void clearTextBuffer() { + if (!scanDtd) { + textBuffer.resize(0); + textBuffer.reserve(256); + } + } + struct Attribute { + Value key; + Value value; + }; + QXmlStreamSimpleStack<Attribute> attributeStack; + + inline QStringRef symString(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + QStringView symView(int index) const + { + const Value &symbol = sym(index); + return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix); + } + inline QStringRef symName(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symString(int index, int offset) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); + } + inline QStringRef symPrefix(int index) { + const Value &symbol = sym(index); + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + inline QStringRef symString(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symPrefix(const Value &symbol) { + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + + inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } + + + short token; + uint token_char; + + uint filterCarriageReturn(); + inline uint getChar(); + inline uint peekChar(); + inline void putChar(uint c) { putStack.push() = c; } + inline void putChar(QChar c) { putStack.push() = c.unicode(); } + void putString(const QString &s, int from = 0); + void putStringLiteral(const QString &s); + void putReplacement(const QString &s); + void putReplacementInAttributeValue(const QString &s); + uint getChar_helper(); + + bool scanUntil(const char *str, short tokenToInject = -1); + bool scanString(const char *str, short tokenToInject, bool requireSpace = true); + inline void injectToken(ushort tokenToInject) { + putChar(int(tokenToInject) << 16); + } + + QString resolveUndeclaredEntity(const QString &name); + void parseEntity(const QString &value); + QXmlStreamReaderPrivate *entityParser; + + bool scanAfterLangleBang(); + bool scanPublicOrSystem(); + bool scanNData(); + bool scanAfterDefaultDecl(); + bool scanAttType(); + + + // scan optimization functions. Not strictly necessary but LALR is + // not very well suited for scanning fast + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); + int fastScanName(int *prefix = 0); + inline int fastScanNMTOKEN(); + + + bool parse(); + inline void consumeRule(int); + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); + + QXmlStreamEntityResolver *entityResolver; + +private: + /*! \internal + Never assign to variable type directly. Instead use this function. + + This prevents errors from being ignored. + */ + inline void setType(const QXmlStreamReader::TokenType t) + { + if(type != QXmlStreamReader::Invalid) + type = t; + } +}; + +bool QXmlStreamReaderPrivate::parse() +{ + // cleanup currently reported token + + switch (type) { + case QXmlStreamReader::StartElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + publicNamespaceDeclarations.clear(); + attributes.clear(); + if (isEmptyElement) { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + isEmptyElement = false; + return true; + } + clearTextBuffer(); + break; + case QXmlStreamReader::EndElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::DTD: + publicNotationDeclarations.clear(); + publicEntityDeclarations.clear(); + dtdName.clear(); + dtdPublicId.clear(); + dtdSystemId.clear(); + Q_FALLTHROUGH(); + case QXmlStreamReader::Comment: + case QXmlStreamReader::Characters: + isCDATA = false; + isWhitespace = true; + text.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::EntityReference: + text.clear(); + name.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::ProcessingInstruction: + processingInstructionTarget.clear(); + processingInstructionData.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + lockEncoding = true; + documentVersion.clear(); + documentEncoding.clear(); +#ifndef QT_NO_TEXTCODEC + if (decoder && decoder->hasFailure()) { + raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return false; + } +#endif + Q_FALLTHROUGH(); + default: + clearTextBuffer(); + ; + } + + setType(QXmlStreamReader::NoToken); + + + // the main parse loop + int act, r; + + if (resumeReduction) { + act = state_stack[tos-1]; + r = resumeReduction; + resumeReduction = 0; + goto ResumeReduction; + } + + act = state_stack[tos]; + + forever { + if (token == -1 && - TERMINAL_COUNT != action_index[act]) { + uint cu = getChar(); + token = NOTOKEN; + token_char = cu == ~0U ? cu : ushort(cu); + if ((cu != ~0U) && (cu & 0xff0000)) { + token = cu >> 16; + } else switch (token_char) { + case 0xfffe: + case 0xffff: + token = ERROR; + break; + case '\r': + token = SPACE; + if (cu == '\r') { + if ((token_char = filterCarriageReturn())) { + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + break; + } + } else { + break; + } + Q_FALLTHROUGH(); + case ~0U: { + token = EOF_SYMBOL; + if (!tagsDone && !inParseEntity) { + int a = t_action(act, token); + if (a < 0) { + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + return false; + } + } + + } break; + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + token = SPACE; + break; + case '&': + token = AMPERSAND; + break; + case '#': + token = HASH; + break; + case '\'': + token = QUOTE; + break; + case '\"': + token = DBLQUOTE; + break; + case '<': + token = LANGLE; + break; + case '>': + token = RANGLE; + break; + case '[': + token = LBRACK; + break; + case ']': + token = RBRACK; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '|': + token = PIPE; + break; + case '=': + token = EQ; + break; + case '%': + token = PERCENT; + break; + case '/': + token = SLASH; + break; + case ':': + token = COLON; + break; + case ';': + token = SEMICOLON; + break; + case ',': + token = COMMA; + break; + case '-': + token = DASH; + break; + case '+': + token = PLUS; + break; + case '*': + token = STAR; + break; + case '.': + token = DOT; + break; + case '?': + token = QUESTIONMARK; + break; + case '!': + token = BANG; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token = DIGIT; + break; + default: + if (cu < 0x20) + token = NOTOKEN; + else + token = LETTER; + break; + } + } + + act = t_action (act, token); + if (act == ACCEPT_STATE) { + // reset the parser in case someone resumes (process instructions can follow a valid document) + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return true; + } else if (act > 0) { + if (++tos == stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; + val.c = token_char; + val.pos = textBuffer.size(); + val.prefix = 0; + val.len = 1; + if (token_char) + textBuffer += QChar(token_char); + + state_stack[tos] = act; + token = -1; + + + } else if (act < 0) { + r = - act - 1; + +#if defined (QLALR_DEBUG) + int ridx = rule_index[r]; + printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); + ++ridx; + for (int i = ridx; i < ridx + rhs[r]; ++i) { + int symbol = rule_info[i]; + if (const char *name = spell[symbol]) + printf (" %s", name); + else + printf (" #%d", symbol); + } + printf ("\n"); +#endif + + tos -= rhs[r]; + act = state_stack[tos++]; + ResumeReduction: + switch (r) { +./ + +document ::= PARSE_ENTITY content; +/. + case $rule_number: + setType(QXmlStreamReader::EndDocument); + break; +./ + +document ::= prolog; +/. + case $rule_number: + if (type != QXmlStreamReader::Invalid) { + if (hasSeenTag || inParseEntity) { + setType(QXmlStreamReader::EndDocument); + } else { + raiseError(QXmlStreamReader::NotWellFormedError, QXmlStream::tr("Start tag expected.")); + // reset the parser + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return false; + } + } + break; +./ + + +prolog ::= prolog stag content etag; +prolog ::= prolog empty_element_tag; +prolog ::= prolog comment; +prolog ::= prolog xml_decl; +prolog ::= prolog processing_instruction; +prolog ::= prolog doctype_decl; +prolog ::= prolog SPACE; +prolog ::=; + +entity_done ::= ENTITY_DONE; +/. + case $rule_number: + entityReferenceStack.pop()->isCurrentlyReferenced = false; + clearSym(); + break; +./ + + +xml_decl_start ::= XML; +/. + case $rule_number: + if (!scanString(spell[VERSION], VERSION, false) && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +xml_decl ::= xml_decl_start VERSION space_opt EQ space_opt literal attribute_list_opt QUESTIONMARK RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::StartDocument); + documentVersion = symString(6); + startDocument(); + break; +./ + +external_id ::= SYSTEM literal; +/. + case $rule_number: + hasExternalDtdSubset = true; + dtdSystemId = symString(2); + break; +./ +external_id ::= PUBLIC public_literal space literal; +/. + case $rule_number: + checkPublicLiteral(symString(2)); + dtdPublicId = symString(2); + dtdSystemId = symString(4); + hasExternalDtdSubset = true; + break; +./ +external_id ::=; + +doctype_decl_start ::= langle_bang DOCTYPE qname space; +/. + case $rule_number: + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + dtdName = symString(3); + break; +./ + +doctype_decl ::= langle_bang DOCTYPE qname RANGLE; +/. + case $rule_number:./ +doctype_decl ::= langle_bang DOCTYPE qname markup space_opt RANGLE; +/. + case $rule_number: + dtdName = symString(3); + // fall through +./ +doctype_decl ::= doctype_decl_start external_id space_opt markup space_opt RANGLE; +/. + case $rule_number:./ +doctype_decl ::= doctype_decl_start external_id space_opt RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::DTD); + text = &textBuffer; + break; +./ + +markup_start ::= LBRACK; +/. + case $rule_number: + scanDtd = true; + break; +./ + +markup ::= markup_start markup_list RBRACK; +/. + case $rule_number: + scanDtd = false; + break; +./ + + +markup_list ::= markup_decl | space | pereference; +markup_list ::= markup_list markup_decl | markup_list space | markup_list pereference; +markup_list ::=; + +markup_decl ::= element_decl | attlist_decl | entity_decl | entity_done | notation_decl | processing_instruction | comment; + + +element_decl_start ::= langle_bang ELEMENT qname space; +/. + case $rule_number: + if (!scanString(spell[EMPTY], EMPTY, false) + && !scanString(spell[ANY], ANY, false) + && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +element_decl ::= element_decl_start content_spec space_opt RANGLE; + + +content_spec ::= EMPTY | ANY | mixed | children; + +pcdata_start ::= HASH; +/. + case $rule_number: + if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +pcdata ::= pcdata_start PCDATA; + +questionmark_or_star_or_plus_opt ::= QUESTIONMARK | STAR | PLUS; +questionmark_or_star_or_plus_opt ::=; + +cp ::= qname questionmark_or_star_or_plus_opt | choice_or_seq questionmark_or_star_or_plus_opt; + +cp_pipe_or_comma_list ::= cp space_opt; +cp_pipe_or_comma_list ::= cp space_opt PIPE space_opt cp_pipe_list space_opt; +cp_pipe_or_comma_list ::= cp space_opt COMMA space_opt cp_comma_list space_opt; +cp_pipe_list ::= cp | cp_pipe_list space_opt PIPE space_opt cp; +cp_comma_list ::= cp | cp_comma_list space_opt COMMA space_opt cp; + + +name_pipe_list ::= PIPE space_opt qname; +name_pipe_list ::= name_pipe_list space_opt PIPE space_opt qname; + +star_opt ::= | STAR; + +mixed ::= LPAREN space_opt pcdata space_opt RPAREN star_opt; +mixed ::= LPAREN space_opt pcdata space_opt name_pipe_list space_opt RPAREN STAR; + +choice_or_seq ::= LPAREN space_opt cp_pipe_or_comma_list RPAREN; + +children ::= choice_or_seq questionmark_or_star_or_plus_opt; + + +nmtoken_pipe_list ::= nmtoken; +nmtoken_pipe_list ::= nmtoken_pipe_list space_opt PIPE space_opt nmtoken; + + +att_type ::= CDATA; +/. + case $rule_number: { + lastAttributeIsCData = true; + } break; +./ +att_type ::= ID | IDREF | IDREFS | ENTITY | ENTITIES | NMTOKEN | NMTOKENS; +att_type ::= LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; +att_type ::= NOTATION LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; + + +default_declhash ::= HASH; +/. + case $rule_number: + if (!scanAfterDefaultDecl() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +default_decl ::= default_declhash REQUIRED; +default_decl ::= default_declhash IMPLIED; +default_decl ::= attribute_value; +default_decl ::= default_declhash FIXED space attribute_value; +attdef_start ::= space qname space; +/. + case $rule_number: + sym(1) = sym(2); + lastAttributeValue.clear(); + lastAttributeIsCData = false; + if (!scanAttType() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +attdef ::= attdef_start att_type default_decl; +/. + case $rule_number: { + DtdAttribute &dtdAttribute = dtdAttributes.push(); + dtdAttribute.tagName.clear(); + dtdAttribute.isCDATA = lastAttributeIsCData; + dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); + dtdAttribute.attributeName = addToStringStorage(symString(1)); + dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); + dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") + || (dtdAttribute.attributePrefix.isEmpty() + && dtdAttribute.attributeName == QLatin1String("xmlns"))); + if (lastAttributeValue.isNull()) { + dtdAttribute.defaultValue.clear(); + } else { + if (dtdAttribute.isCDATA) + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); + else + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); + + } + } break; +./ + +attdef_list ::= attdef; +attdef_list ::= attdef_list attdef; + +attlist_decl ::= langle_bang ATTLIST qname space_opt RANGLE; +attlist_decl ::= langle_bang ATTLIST qname attdef_list space_opt RANGLE; +/. + case $rule_number: { + if (referenceToUnparsedEntityDetected && !standalone) + break; + int n = dtdAttributes.size(); + QStringRef tagName = addToStringStorage(symName(3)); + while (n--) { + DtdAttribute &dtdAttribute = dtdAttributes[n]; + if (!dtdAttribute.tagName.isNull()) + break; + dtdAttribute.tagName = tagName; + for (int i = 0; i < n; ++i) { + if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) + && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { + dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it + break; + } + } + } + } break; +./ + +entity_decl_start ::= langle_bang ENTITY name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(3); + } break; +./ + +entity_decl_start ::= langle_bang ENTITY PERCENT space name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(5); + entityDeclaration.parameter = true; + } break; +./ + +entity_decl_external ::= entity_decl_start SYSTEM literal; +/. + case $rule_number: { + if (!scanNData() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.systemId = symString(3); + entityDeclaration.external = true; + } break; +./ + +entity_decl_external ::= entity_decl_start PUBLIC public_literal space literal; +/. + case $rule_number: { + if (!scanNData() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + checkPublicLiteral((entityDeclaration.publicId = symString(3))); + entityDeclaration.systemId = symString(5); + entityDeclaration.external = true; + } break; +./ + +entity_decl ::= entity_decl_external NDATA name space_opt RANGLE; +/. + case $rule_number: { + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.notationName = symString(3); + if (entityDeclaration.parameter) + raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); + } + Q_FALLTHROUGH(); +./ + +entity_decl ::= entity_decl_external space_opt RANGLE; +/. + case $rule_number:./ + +entity_decl ::= entity_decl_start entity_value space_opt RANGLE; +/. + case $rule_number: { + if (referenceToUnparsedEntityDetected && !standalone) { + entityDeclarations.pop(); + break; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + if (!entityDeclaration.external) + entityDeclaration.value = symString(2); + auto &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; + if (!hash.contains(qToStringViewIgnoringNull(entityDeclaration.name))) { + Entity entity(entityDeclaration.name.toString(), + entityDeclaration.value.toString()); + entity.unparsed = (!entityDeclaration.notationName.isNull()); + entity.external = entityDeclaration.external; + hash.insert(qToStringViewIgnoringNull(entity.name), entity); + } + } break; +./ + + +processing_instruction ::= LANGLE QUESTIONMARK name space; +/. + case $rule_number: { + setType(QXmlStreamReader::ProcessingInstruction); + int pos = sym(4).pos + sym(4).len; + processingInstructionTarget = symString(3); + if (scanUntil("?>")) { + processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { + raiseWellFormedError(QXmlStream::tr("XML declaration not at start of document.")); + } + else if (!QXmlUtils::isNCName(processingInstructionTarget)) + raiseWellFormedError(QXmlStream::tr("%1 is an invalid processing instruction name.") + .arg(processingInstructionTarget)); + } else if (type != QXmlStreamReader::Invalid){ + resume($rule_number); + return false; + } + } break; +./ + +processing_instruction ::= LANGLE QUESTIONMARK name QUESTIONMARK RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::ProcessingInstruction); + processingInstructionTarget = symString(3); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) + raiseWellFormedError(QXmlStream::tr("Invalid processing instruction name.")); + break; +./ + + +langle_bang ::= LANGLE BANG; +/. + case $rule_number: + if (!scanAfterLangleBang() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +comment_start ::= langle_bang DASH DASH; +/. + case $rule_number: + if (!scanUntil("--")) { + resume($rule_number); + return false; + } + break; +./ + +comment ::= comment_start RANGLE; +/. + case $rule_number: { + setType(QXmlStreamReader::Comment); + int pos = sym(1).pos + 4; + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } break; +./ + + +cdata ::= langle_bang CDATA_START; +/. + case $rule_number: { + setType(QXmlStreamReader::Characters); + isCDATA = true; + isWhitespace = false; + int pos = sym(2).pos; + if (scanUntil("]]>", -1)) { + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } else { + resume($rule_number); + return false; + } + } break; +./ + +notation_decl_start ::= langle_bang NOTATION name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); + notationDeclaration.name = symString(3); + } break; +./ + +notation_decl ::= notation_decl_start SYSTEM literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId = symString(3); + notationDeclaration.publicId.clear(); + } break; +./ + +notation_decl ::= notation_decl_start PUBLIC public_literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId.clear(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + } break; +./ + +notation_decl ::= notation_decl_start PUBLIC public_literal space literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + notationDeclaration.systemId = symString(5); + } break; +./ + + + +content_char ::= RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG | QUOTE | DBLQUOTE | LETTER | DIGIT; + +scan_content_char ::= content_char; +/. + case $rule_number: + isWhitespace = false; + Q_FALLTHROUGH(); +./ + +scan_content_char ::= SPACE; +/. + case $rule_number: + sym(1).len += fastScanContentCharList(); + if (atEnd && !inParseEntity) { + resume($rule_number); + return false; + } + break; +./ + +content_char_list ::= content_char_list char_ref; +content_char_list ::= content_char_list entity_ref; +content_char_list ::= content_char_list entity_done; +content_char_list ::= content_char_list scan_content_char; +content_char_list ::= char_ref; +content_char_list ::= entity_ref; +content_char_list ::= entity_done; +content_char_list ::= scan_content_char; + + +character_content ::= content_char_list %prec SHIFT_THERE; +/. + case $rule_number: + if (!textBuffer.isEmpty()) { + setType(QXmlStreamReader::Characters); + text = &textBuffer; + } + break; +./ + +literal ::= QUOTE QUOTE; +/. + case $rule_number:./ +literal ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ +literal ::= QUOTE literal_content_with_dblquote QUOTE; +/. + case $rule_number:./ +literal ::= DBLQUOTE literal_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + break; +./ + +literal_content_with_dblquote ::= literal_content_with_dblquote literal_content; +/. + case $rule_number:./ +literal_content_with_quote ::= literal_content_with_quote literal_content; +/. + case $rule_number:./ +literal_content_with_dblquote ::= literal_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +literal_content_with_quote ::= literal_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +literal_content_with_dblquote ::= literal_content; +literal_content_with_quote ::= literal_content; +literal_content_with_dblquote ::= DBLQUOTE; +literal_content_with_quote ::= QUOTE; + +literal_content_start ::= LETTER | DIGIT | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; + +literal_content_start ::= SPACE; +/. + case $rule_number: + if (normalizeLiterals) + textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); + break; +./ + +literal_content ::= literal_content_start; +/. + case $rule_number: + sym(1).len += fastScanLiteralContent(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + + +public_literal ::= literal; +/. + case $rule_number: { + if (!QXmlUtils::isPublicID(symString(1))) { + raiseWellFormedError(QXmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1))); + resume($rule_number); + return false; + } + } break; +./ + +entity_value ::= QUOTE QUOTE; +/. + case $rule_number:./ +entity_value ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ + +entity_value ::= QUOTE entity_value_content_with_dblquote QUOTE; +/. + case $rule_number:./ +entity_value ::= DBLQUOTE entity_value_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + break; +./ + +entity_value_content_with_dblquote ::= entity_value_content_with_dblquote entity_value_content; +/. + case $rule_number:./ +entity_value_content_with_quote ::= entity_value_content_with_quote entity_value_content; +/. + case $rule_number:./ +entity_value_content_with_dblquote ::= entity_value_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +entity_value_content_with_quote ::= entity_value_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +entity_value_content_with_dblquote ::= entity_value_content; +entity_value_content_with_quote ::= entity_value_content; +entity_value_content_with_dblquote ::= DBLQUOTE; +entity_value_content_with_quote ::= QUOTE; + +entity_value_content ::= LETTER | DIGIT | LANGLE | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | SLASH | COLON | SEMICOLON | COMMA | SPACE | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; +entity_value_content ::= char_ref | entity_ref_in_entity_value | entity_done; + + +attribute_value ::= QUOTE QUOTE; +/. + case $rule_number:./ +attribute_value ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ +attribute_value ::= QUOTE attribute_value_content_with_dblquote QUOTE; +/. + case $rule_number:./ +attribute_value ::= DBLQUOTE attribute_value_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + lastAttributeValue = symString(1); + break; +./ + +attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote attribute_value_content; +/. + case $rule_number:./ +attribute_value_content_with_quote ::= attribute_value_content_with_quote attribute_value_content; +/. + case $rule_number:./ +attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +attribute_value_content_with_quote ::= attribute_value_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +attribute_value_content_with_dblquote ::= attribute_value_content | DBLQUOTE; +attribute_value_content_with_quote ::= attribute_value_content | QUOTE; + +attribute_value_content ::= literal_content | char_ref | entity_ref_in_attribute_value | entity_done; + +attribute ::= qname space_opt EQ space_opt attribute_value; +/. + case $rule_number: { + QStringRef prefix = symPrefix(1); + if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(symString(5)); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = addToStringStorage(ns); + } else { + Attribute &attribute = attributeStack.push(); + attribute.key = sym(1); + attribute.value = sym(5); + + QStringRef attributeQualifiedName = symName(1); + bool normalize = false; + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isCDATA + && dtdAttribute.tagName == qualifiedName + && dtdAttribute.attributeQualifiedName == attributeQualifiedName + ) { + normalize = true; + break; + } + } + if (normalize) { + // normalize attribute value (simplify and trim) + int pos = textBuffer.size(); + int n = 0; + bool wasSpace = true; + for (int i = 0; i < attribute.value.len; ++i) { + QChar c = textBuffer.at(attribute.value.pos + i); + if (c.unicode() == ' ') { + if (wasSpace) + continue; + wasSpace = true; + } else { + wasSpace = false; + } + textBuffer += textBuffer.at(attribute.value.pos + i); + ++n; + } + if (wasSpace) + while (n && textBuffer.at(pos + n - 1).unicode() == ' ') + --n; + attribute.value.pos = pos; + attribute.value.len = n; + } + if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = symString(attribute.key); + QStringRef namespaceUri = symString(attribute.value); + attributeStack.pop(); + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + } + } + } break; +./ + + + +attribute_list_opt ::= | space | space attribute_list space_opt; +attribute_list ::= attribute | attribute_list space attribute; + +stag_start ::= LANGLE qname; +/. + case $rule_number: { + normalizeLiterals = true; + Tag &tag = tagStack_push(); + prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); + name = tag.name = addToStringStorage(symString(2)); + qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); + if ((!prefix.isEmpty() && !QXmlUtils::isNCName(prefix)) || !QXmlUtils::isNCName(name)) + raiseWellFormedError(QXmlStream::tr("Invalid XML name.")); + } break; +./ + + +empty_element_tag ::= stag_start attribute_list_opt SLASH RANGLE; +/. + case $rule_number: + isEmptyElement = true; + Q_FALLTHROUGH(); +./ + + +stag ::= stag_start attribute_list_opt RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::StartElement); + resolveTag(); + if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) + raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); + hasSeenTag = true; + break; +./ + + +etag ::= LANGLE SLASH qname space_opt RANGLE; +/. + case $rule_number: { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + if (qualifiedName != symName(3)) + raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch.")); + } break; +./ + + +unresolved_entity ::= UNRESOLVED_ENTITY; +/. + case $rule_number: + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); + break; + } + setType(QXmlStreamReader::EntityReference); + name = &unresolvedEntity; + break; +./ + +entity_ref ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed) { + raiseWellFormedError(QXmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); + } else { + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacement(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + + injectToken(UNRESOLVED_ENTITY); + unresolvedEntity = symString(2).toString(); + textBuffer.chop(2 + sym(2).len); + clearSym(); + + } break; +./ + +pereference ::= PERCENT name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (parameterEntityHash.contains(reference)) { + referenceToParameterEntityDetected = true; + Entity &entity = parameterEntityHash[reference]; + if (entity.unparsed || entity.external) { + referenceToUnparsedEntityDetected = true; + } else { + if (referenceEntity(entity)) + putString(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + } else if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(symString(2))); + } + } break; +./ + + + +entity_ref_in_entity_value ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: + sym(1).len += sym(2).len + 1; + break; +./ + +entity_ref_in_attribute_value ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed || entity.value.isNull()) { + raiseWellFormedError(QXmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); + break; + } + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacementInAttributeValue(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(reference)); + } + } break; +./ + +char_ref ::= AMPERSAND HASH char_ref_value SEMICOLON; +/. + case $rule_number: { + if (uint s = resolveCharRef(3)) { + if (s >= 0xffff) + putStringLiteral(QString::fromUcs4(&s, 1)); + else + putChar((LETTER << 16) | s); + + textBuffer.chop(3 + sym(3).len); + clearSym(); + } else { + raiseWellFormedError(QXmlStream::tr("Invalid character reference.")); + } + } break; +./ + + +char_ref_value ::= LETTER | DIGIT; +char_ref_value ::= char_ref_value LETTER; +/. + case $rule_number:./ +char_ref_value ::= char_ref_value DIGIT; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ + + +content ::= content character_content; +content ::= content stag content etag; +content ::= content empty_element_tag; +content ::= content comment; +content ::= content cdata; +content ::= content xml_decl; +content ::= content processing_instruction; +content ::= content doctype_decl; +content ::= content unresolved_entity; +content ::= ; + + +space ::= SPACE; +/. + case $rule_number: + sym(1).len += fastScanSpace(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + + +space_opt ::=; +space_opt ::= space; + +qname ::= LETTER; +/. + case $rule_number: { + sym(1).len += fastScanName(&sym(1).prefix); + if (atEnd) { + resume($rule_number); + return false; + } + } break; +./ + +name ::= LETTER; +/. + case $rule_number: + sym(1).len += fastScanName(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + +nmtoken ::= LETTER; +/. + case $rule_number:./ +nmtoken ::= DIGIT; +/. + case $rule_number:./ +nmtoken ::= DOT; +/. + case $rule_number:./ +nmtoken ::= DASH; +/. + case $rule_number:./ +nmtoken ::= COLON; +/. + case $rule_number: + sym(1).len += fastScanNMTOKEN(); + if (atEnd) { + resume($rule_number); + return false; + } + + break; +./ + + +/. + default: + ; + } // switch + act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); + if (type != QXmlStreamReader::NoToken) + return true; + } else { + parseError(); + break; + } + } + return false; +} +#endif //QT_NO_XMLSTREAMREADER.xml + +./ diff --git a/src/corelib/serialization/qxmlstream.h b/src/corelib/serialization/qxmlstream.h new file mode 100644 index 0000000000..2350d12dd6 --- /dev/null +++ b/src/corelib/serialization/qxmlstream.h @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXMLSTREAM_H +#define QXMLSTREAM_H + +#include <QtCore/qiodevice.h> + +#ifndef QT_NO_XMLSTREAM + +#include <QtCore/qstring.h> +#include <QtCore/qvector.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_NAMESPACE + + +class Q_CORE_EXPORT QXmlStreamStringRef { + QString m_string; + int m_position, m_size; +public: + inline QXmlStreamStringRef():m_position(0), m_size(0){} + inline QXmlStreamStringRef(const QStringRef &aString) + :m_string(aString.string()?*aString.string():QString()), m_position(aString.position()), m_size(aString.size()){} + QXmlStreamStringRef(const QString &aString) : m_string(aString), m_position(0), m_size(m_string.size()) {} +#ifdef Q_COMPILER_RVALUE_REFS + QXmlStreamStringRef(QString &&aString) Q_DECL_NOTHROW : m_string(std::move(aString)), m_position(0), m_size(m_string.size()) {} +#endif + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + QXmlStreamStringRef(const QXmlStreamStringRef &other) // = default + : m_string(other.m_string), m_position(other.m_position), m_size(other.m_size) {} +#ifdef Q_COMPILER_RVALUE_REFS + QXmlStreamStringRef(QXmlStreamStringRef &&other) Q_DECL_NOTHROW // = default + : m_string(std::move(other.m_string)), m_position(other.m_position), m_size(other.m_size) {} + QXmlStreamStringRef &operator=(QXmlStreamStringRef &&other) Q_DECL_NOTHROW // = default + { swap(other); return *this; } +#endif + QXmlStreamStringRef &operator=(const QXmlStreamStringRef &other) // = default + { m_string = other.m_string; m_position = other.m_position; m_size = other.m_size; return *this; } + inline ~QXmlStreamStringRef() {} // ### this prevents (or deprecates) all the move/copy special member functions, + // ### that's why we need to provide them by hand above. We can't remove it in + // ### Qt 5, since that would change the way its passed to functions. In Qt 6, remove all. +#endif // Qt < 6.0 + + void swap(QXmlStreamStringRef &other) Q_DECL_NOTHROW + { + qSwap(m_string, other.m_string); + qSwap(m_position, other.m_position); + qSwap(m_size, other.m_size); + } + + inline void clear() { m_string.clear(); m_position = m_size = 0; } + inline operator QStringRef() const { return QStringRef(&m_string, m_position, m_size); } + inline const QString *string() const { return &m_string; } + inline int position() const { return m_position; } + inline int size() const { return m_size; } +}; +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QXmlStreamStringRef) + + +class QXmlStreamReaderPrivate; +class QXmlStreamAttributes; +class Q_CORE_EXPORT QXmlStreamAttribute { + QXmlStreamStringRef m_name, m_namespaceUri, m_qualifiedName, m_value; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + uint m_isDefault : 1; + friend class QXmlStreamReaderPrivate; + friend class QXmlStreamAttributes; +public: + QXmlStreamAttribute(); + QXmlStreamAttribute(const QString &qualifiedName, const QString &value); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value); + QXmlStreamAttribute(const QXmlStreamAttribute &); +#ifdef Q_COMPILER_RVALUE_REFS + QXmlStreamAttribute(QXmlStreamAttribute &&other) Q_DECL_NOTHROW // = default; + : m_name(std::move(other.m_name)), + m_namespaceUri(std::move(other.m_namespaceUri)), + m_qualifiedName(std::move(other.m_qualifiedName)), + m_value(std::move(other.m_value)), + reserved(other.reserved), + m_isDefault(other.m_isDefault) + { + other.reserved = nullptr; + } + QXmlStreamAttribute &operator=(QXmlStreamAttribute &&other) Q_DECL_NOTHROW // = default; + { + m_name = std::move(other.m_name); + m_namespaceUri = std::move(other.m_namespaceUri); + m_qualifiedName = std::move(other.m_qualifiedName); + m_value = std::move(other.m_value); + qSwap(reserved, other.reserved); + m_isDefault = other.m_isDefault; + return *this; + } +#endif + QXmlStreamAttribute& operator=(const QXmlStreamAttribute &); + ~QXmlStreamAttribute(); +#endif // < Qt 6 + + inline QStringRef namespaceUri() const { return m_namespaceUri; } + inline QStringRef name() const { return m_name; } + inline QStringRef qualifiedName() const { return m_qualifiedName; } + inline QStringRef prefix() const { + return QStringRef(m_qualifiedName.string(), + m_qualifiedName.position(), + qMax(0, m_qualifiedName.size() - m_name.size() - 1)); + } + inline QStringRef value() const { return m_value; } + inline bool isDefault() const { return m_isDefault; } + inline bool operator==(const QXmlStreamAttribute &other) const { + return (value() == other.value() + && (namespaceUri().isNull() ? (qualifiedName() == other.qualifiedName()) + : (namespaceUri() == other.namespaceUri() && name() == other.name()))); + } + inline bool operator!=(const QXmlStreamAttribute &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamAttribute, Q_MOVABLE_TYPE); + +class Q_CORE_EXPORT QXmlStreamAttributes : public QVector<QXmlStreamAttribute> +{ +public: + inline QXmlStreamAttributes() {} + QStringRef value(const QString &namespaceUri, const QString &name) const; + QStringRef value(const QString &namespaceUri, QLatin1String name) const; + QStringRef value(QLatin1String namespaceUri, QLatin1String name) const; + QStringRef value(const QString &qualifiedName) const; + QStringRef value(QLatin1String qualifiedName) const; + void append(const QString &namespaceUri, const QString &name, const QString &value); + void append(const QString &qualifiedName, const QString &value); + + inline bool hasAttribute(const QString &qualifiedName) const + { + return !value(qualifiedName).isNull(); + } + + inline bool hasAttribute(QLatin1String qualifiedName) const + { + return !value(qualifiedName).isNull(); + } + + inline bool hasAttribute(const QString &namespaceUri, const QString &name) const + { + return !value(namespaceUri, name).isNull(); + } + + using QVector<QXmlStreamAttribute>::append; +}; + +class Q_CORE_EXPORT QXmlStreamNamespaceDeclaration { + QXmlStreamStringRef m_prefix, m_namespaceUri; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + + friend class QXmlStreamReaderPrivate; +public: + QXmlStreamNamespaceDeclaration(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &); + QXmlStreamNamespaceDeclaration(QXmlStreamNamespaceDeclaration &&other) Q_DECL_NOTHROW // = default + : m_prefix(std::move(other.m_prefix)), + m_namespaceUri(std::move(other.m_namespaceUri)), + reserved(other.reserved) + { + other.reserved = nullptr; + } + QXmlStreamNamespaceDeclaration &operator=(QXmlStreamNamespaceDeclaration &&other) Q_DECL_NOTHROW // = default + { + m_prefix = std::move(other.m_prefix); + m_namespaceUri = std::move(other.m_namespaceUri); + qSwap(reserved, other.reserved); + return *this; + } + QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri); + ~QXmlStreamNamespaceDeclaration(); + QXmlStreamNamespaceDeclaration& operator=(const QXmlStreamNamespaceDeclaration &); +#endif // < Qt 6 + + inline QStringRef prefix() const { return m_prefix; } + inline QStringRef namespaceUri() const { return m_namespaceUri; } + inline bool operator==(const QXmlStreamNamespaceDeclaration &other) const { + return (prefix() == other.prefix() && namespaceUri() == other.namespaceUri()); + } + inline bool operator!=(const QXmlStreamNamespaceDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamNamespaceDeclaration, Q_MOVABLE_TYPE); +typedef QVector<QXmlStreamNamespaceDeclaration> QXmlStreamNamespaceDeclarations; + +class Q_CORE_EXPORT QXmlStreamNotationDeclaration { + QXmlStreamStringRef m_name, m_systemId, m_publicId; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + + friend class QXmlStreamReaderPrivate; +public: + QXmlStreamNotationDeclaration(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ~QXmlStreamNotationDeclaration(); + QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &); + QXmlStreamNotationDeclaration(QXmlStreamNotationDeclaration &&other) Q_DECL_NOTHROW // = default + : m_name(std::move(other.m_name)), + m_systemId(std::move(other.m_systemId)), + m_publicId(std::move(other.m_publicId)), + reserved(other.reserved) + { + other.reserved = nullptr; + } + QXmlStreamNotationDeclaration& operator=(const QXmlStreamNotationDeclaration &); + QXmlStreamNotationDeclaration &operator=(QXmlStreamNotationDeclaration &&other) Q_DECL_NOTHROW // = default + { + m_name = std::move(other.m_name); + m_systemId = std::move(other.m_systemId); + m_publicId = std::move(other.m_publicId); + qSwap(reserved, other.reserved); + return *this; + } +#endif // < Qt 6 + + inline QStringRef name() const { return m_name; } + inline QStringRef systemId() const { return m_systemId; } + inline QStringRef publicId() const { return m_publicId; } + inline bool operator==(const QXmlStreamNotationDeclaration &other) const { + return (name() == other.name() && systemId() == other.systemId() + && publicId() == other.publicId()); + } + inline bool operator!=(const QXmlStreamNotationDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamNotationDeclaration, Q_MOVABLE_TYPE); +typedef QVector<QXmlStreamNotationDeclaration> QXmlStreamNotationDeclarations; + +class Q_CORE_EXPORT QXmlStreamEntityDeclaration { + QXmlStreamStringRef m_name, m_notationName, m_systemId, m_publicId, m_value; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + + friend class QXmlStreamReaderPrivate; +public: + QXmlStreamEntityDeclaration(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ~QXmlStreamEntityDeclaration(); + QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &); + QXmlStreamEntityDeclaration(QXmlStreamEntityDeclaration &&other) Q_DECL_NOTHROW // = default + : m_name(std::move(other.m_name)), + m_notationName(std::move(other.m_notationName)), + m_systemId(std::move(other.m_systemId)), + m_publicId(std::move(other.m_publicId)), + m_value(std::move(other.m_value)), + reserved(other.reserved) + { + other.reserved = nullptr; + } + QXmlStreamEntityDeclaration& operator=(const QXmlStreamEntityDeclaration &); + QXmlStreamEntityDeclaration &operator=(QXmlStreamEntityDeclaration &&other) Q_DECL_NOTHROW // = default + { + m_name = std::move(other.m_name); + m_notationName = std::move(other.m_notationName); + m_systemId = std::move(other.m_systemId); + m_publicId = std::move(other.m_publicId); + m_value = std::move(other.m_value); + qSwap(reserved, other.reserved); + return *this; + } +#endif // < Qt 6 + + inline QStringRef name() const { return m_name; } + inline QStringRef notationName() const { return m_notationName; } + inline QStringRef systemId() const { return m_systemId; } + inline QStringRef publicId() const { return m_publicId; } + inline QStringRef value() const { return m_value; } + inline bool operator==(const QXmlStreamEntityDeclaration &other) const { + return (name() == other.name() + && notationName() == other.notationName() + && systemId() == other.systemId() + && publicId() == other.publicId() + && value() == other.value()); + } + inline bool operator!=(const QXmlStreamEntityDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamEntityDeclaration, Q_MOVABLE_TYPE); +typedef QVector<QXmlStreamEntityDeclaration> QXmlStreamEntityDeclarations; + + +class Q_CORE_EXPORT QXmlStreamEntityResolver +{ +public: + virtual ~QXmlStreamEntityResolver(); + virtual QString resolveEntity(const QString& publicId, const QString& systemId); + virtual QString resolveUndeclaredEntity(const QString &name); +}; + +#ifndef QT_NO_XMLSTREAMREADER +class Q_CORE_EXPORT QXmlStreamReader { + QDOC_PROPERTY(bool namespaceProcessing READ namespaceProcessing WRITE setNamespaceProcessing) +public: + enum TokenType { + NoToken = 0, + Invalid, + StartDocument, + EndDocument, + StartElement, + EndElement, + Characters, + Comment, + DTD, + EntityReference, + ProcessingInstruction + }; + + + QXmlStreamReader(); + explicit QXmlStreamReader(QIODevice *device); + explicit QXmlStreamReader(const QByteArray &data); + explicit QXmlStreamReader(const QString &data); + explicit QXmlStreamReader(const char * data); + ~QXmlStreamReader(); + + void setDevice(QIODevice *device); + QIODevice *device() const; + void addData(const QByteArray &data); + void addData(const QString &data); + void addData(const char *data); + void clear(); + + + bool atEnd() const; + TokenType readNext(); + + bool readNextStartElement(); + void skipCurrentElement(); + + TokenType tokenType() const; + QString tokenString() const; + + void setNamespaceProcessing(bool); + bool namespaceProcessing() const; + + inline bool isStartDocument() const { return tokenType() == StartDocument; } + inline bool isEndDocument() const { return tokenType() == EndDocument; } + inline bool isStartElement() const { return tokenType() == StartElement; } + inline bool isEndElement() const { return tokenType() == EndElement; } + inline bool isCharacters() const { return tokenType() == Characters; } + bool isWhitespace() const; + bool isCDATA() const; + inline bool isComment() const { return tokenType() == Comment; } + inline bool isDTD() const { return tokenType() == DTD; } + inline bool isEntityReference() const { return tokenType() == EntityReference; } + inline bool isProcessingInstruction() const { return tokenType() == ProcessingInstruction; } + + bool isStandaloneDocument() const; + QStringRef documentVersion() const; + QStringRef documentEncoding() const; + + qint64 lineNumber() const; + qint64 columnNumber() const; + qint64 characterOffset() const; + + QXmlStreamAttributes attributes() const; + + enum ReadElementTextBehaviour { + ErrorOnUnexpectedElement, + IncludeChildElements, + SkipChildElements + }; + QString readElementText(ReadElementTextBehaviour behaviour = ErrorOnUnexpectedElement); + + QStringRef name() const; + QStringRef namespaceUri() const; + QStringRef qualifiedName() const; + QStringRef prefix() const; + + QStringRef processingInstructionTarget() const; + QStringRef processingInstructionData() const; + + QStringRef text() const; + + QXmlStreamNamespaceDeclarations namespaceDeclarations() const; + void addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaraction); + void addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclaractions); + QXmlStreamNotationDeclarations notationDeclarations() const; + QXmlStreamEntityDeclarations entityDeclarations() const; + QStringRef dtdName() const; + QStringRef dtdPublicId() const; + QStringRef dtdSystemId() const; + + + enum Error { + NoError, + UnexpectedElementError, + CustomError, + NotWellFormedError, + PrematureEndOfDocumentError + }; + void raiseError(const QString& message = QString()); + QString errorString() const; + Error error() const; + + inline bool hasError() const + { + return error() != NoError; + } + + void setEntityResolver(QXmlStreamEntityResolver *resolver); + QXmlStreamEntityResolver *entityResolver() const; + +private: + Q_DISABLE_COPY(QXmlStreamReader) + Q_DECLARE_PRIVATE(QXmlStreamReader) + QScopedPointer<QXmlStreamReaderPrivate> d_ptr; + +}; +#endif // QT_NO_XMLSTREAMREADER + +#ifndef QT_NO_XMLSTREAMWRITER + +class QXmlStreamWriterPrivate; + +class Q_CORE_EXPORT QXmlStreamWriter +{ + QDOC_PROPERTY(bool autoFormatting READ autoFormatting WRITE setAutoFormatting) + QDOC_PROPERTY(int autoFormattingIndent READ autoFormattingIndent WRITE setAutoFormattingIndent) +public: + QXmlStreamWriter(); + explicit QXmlStreamWriter(QIODevice *device); + explicit QXmlStreamWriter(QByteArray *array); + explicit QXmlStreamWriter(QString *string); + ~QXmlStreamWriter(); + + void setDevice(QIODevice *device); + QIODevice *device() const; + +#ifndef QT_NO_TEXTCODEC + void setCodec(QTextCodec *codec); + void setCodec(const char *codecName); + QTextCodec *codec() const; +#endif + + void setAutoFormatting(bool); + bool autoFormatting() const; + + void setAutoFormattingIndent(int spacesOrTabs); + int autoFormattingIndent() const; + + void writeAttribute(const QString &qualifiedName, const QString &value); + void writeAttribute(const QString &namespaceUri, const QString &name, const QString &value); + void writeAttribute(const QXmlStreamAttribute& attribute); + void writeAttributes(const QXmlStreamAttributes& attributes); + + void writeCDATA(const QString &text); + void writeCharacters(const QString &text); + void writeComment(const QString &text); + + void writeDTD(const QString &dtd); + + void writeEmptyElement(const QString &qualifiedName); + void writeEmptyElement(const QString &namespaceUri, const QString &name); + + void writeTextElement(const QString &qualifiedName, const QString &text); + void writeTextElement(const QString &namespaceUri, const QString &name, const QString &text); + + void writeEndDocument(); + void writeEndElement(); + + void writeEntityReference(const QString &name); + void writeNamespace(const QString &namespaceUri, const QString &prefix = QString()); + void writeDefaultNamespace(const QString &namespaceUri); + void writeProcessingInstruction(const QString &target, const QString &data = QString()); + + void writeStartDocument(); + void writeStartDocument(const QString &version); + void writeStartDocument(const QString &version, bool standalone); + void writeStartElement(const QString &qualifiedName); + void writeStartElement(const QString &namespaceUri, const QString &name); + +#ifndef QT_NO_XMLSTREAMREADER + void writeCurrentToken(const QXmlStreamReader &reader); +#endif + + bool hasError() const; + +private: + Q_DISABLE_COPY(QXmlStreamWriter) + Q_DECLARE_PRIVATE(QXmlStreamWriter) + QScopedPointer<QXmlStreamWriterPrivate> d_ptr; +}; +#endif // QT_NO_XMLSTREAMWRITER + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAM +#endif // QXMLSTREAM_H diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h new file mode 100644 index 0000000000..5645d812eb --- /dev/null +++ b/src/corelib/serialization/qxmlstream_p.h @@ -0,0 +1,1972 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QXMLSTREAM_P_H +#define QXMLSTREAM_P_H + +#if defined(ERROR) +# undef ERROR +#endif + +class QXmlStreamReader_Table +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + AMPERSAND = 5, + ANY = 41, + ATTLIST = 31, + BANG = 25, + CDATA = 47, + CDATA_START = 28, + COLON = 17, + COMMA = 19, + DASH = 20, + DBLQUOTE = 8, + DIGIT = 27, + DOCTYPE = 29, + DOT = 23, + ELEMENT = 30, + EMPTY = 40, + ENTITIES = 51, + ENTITY = 32, + ENTITY_DONE = 45, + EQ = 14, + ERROR = 43, + FIXED = 39, + HASH = 6, + ID = 48, + IDREF = 49, + IDREFS = 50, + IMPLIED = 38, + LANGLE = 3, + LBRACK = 9, + LETTER = 26, + LPAREN = 11, + NDATA = 36, + NMTOKEN = 52, + NMTOKENS = 53, + NOTATION = 33, + NOTOKEN = 1, + PARSE_ENTITY = 44, + PCDATA = 42, + PERCENT = 15, + PIPE = 13, + PLUS = 21, + PUBLIC = 35, + QUESTIONMARK = 24, + QUOTE = 7, + RANGLE = 4, + RBRACK = 10, + REQUIRED = 37, + RPAREN = 12, + SEMICOLON = 18, + SHIFT_THERE = 56, + SLASH = 16, + SPACE = 2, + STAR = 22, + SYSTEM = 34, + UNRESOLVED_ENTITY = 46, + VERSION = 55, + XML = 54, + + ACCEPT_STATE = 416, + RULE_COUNT = 270, + STATE_COUNT = 427, + TERMINAL_COUNT = 57, + NON_TERMINAL_COUNT = 84, + + GOTO_INDEX_OFFSET = 427, + GOTO_INFO_OFFSET = 1017, + GOTO_CHECK_OFFSET = 1017 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +const char *const QXmlStreamReader_Table::spell [] = { + "end of file", 0, " ", "<", ">", "&", "#", "\'", "\"", "[", + "]", "(", ")", "|", "=", "%", "/", ":", ";", ",", + "-", "+", "*", ".", "?", "!", "[a-zA-Z]", "[0-9]", "[CDATA[", "DOCTYPE", + "ELEMENT", "ATTLIST", "ENTITY", "NOTATION", "SYSTEM", "PUBLIC", "NDATA", "REQUIRED", "IMPLIED", "FIXED", + "EMPTY", "ANY", "PCDATA", 0, 0, 0, 0, "CDATA", "ID", "IDREF", + "IDREFS", "ENTITIES", "NMTOKEN", "NMTOKENS", "<?xml", "version", 0}; + +const short QXmlStreamReader_Table::lhs [] = { + 57, 57, 59, 59, 59, 59, 59, 59, 59, 59, + 67, 68, 64, 72, 72, 72, 75, 66, 66, 66, + 66, 79, 78, 80, 80, 80, 80, 80, 80, 80, + 81, 81, 81, 81, 81, 81, 81, 87, 83, 88, + 88, 88, 88, 91, 92, 93, 93, 93, 93, 94, + 94, 96, 96, 96, 97, 97, 98, 98, 99, 99, + 100, 100, 89, 89, 95, 90, 101, 101, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 104, 105, + 105, 105, 105, 107, 108, 109, 109, 84, 84, 110, + 110, 112, 112, 85, 85, 85, 65, 65, 76, 114, + 63, 115, 116, 86, 86, 86, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 118, + 118, 119, 119, 119, 119, 119, 119, 119, 119, 122, + 70, 70, 70, 70, 123, 124, 123, 124, 123, 124, + 123, 124, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 125, 73, 113, 113, 113, 113, + 127, 128, 127, 128, 127, 128, 127, 128, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 106, 106, 106, 106, 131, 132, 131, + 132, 131, 131, 132, 132, 133, 133, 133, 133, 135, + 71, 71, 71, 136, 136, 137, 62, 60, 61, 138, + 121, 82, 130, 134, 120, 139, 139, 139, 139, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 74, + 69, 69, 77, 111, 102, 102, 102, 102, 102, 140}; + +const short QXmlStreamReader_Table::rhs [] = { + 2, 1, 4, 2, 2, 2, 2, 2, 2, 0, + 1, 1, 9, 2, 4, 0, 4, 4, 6, 6, + 4, 1, 3, 1, 1, 1, 2, 2, 2, 0, + 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, + 1, 1, 1, 1, 2, 1, 1, 1, 0, 2, + 2, 2, 6, 6, 1, 5, 1, 5, 3, 5, + 0, 1, 6, 8, 4, 2, 1, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 6, 7, 1, 2, + 2, 1, 4, 3, 3, 1, 2, 5, 6, 4, + 6, 3, 5, 5, 3, 4, 4, 5, 2, 3, + 2, 2, 4, 5, 5, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 2, 2, 3, 3, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, + 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 5, + 0, 1, 3, 1, 3, 2, 4, 3, 5, 1, + 3, 3, 3, 3, 4, 1, 1, 2, 2, 2, + 4, 2, 2, 2, 2, 2, 2, 2, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 2}; + +const short QXmlStreamReader_Table::action_default [] = { + 10, 259, 0, 2, 1, 0, 125, 117, 119, 120, + 127, 129, 123, 11, 114, 108, 0, 109, 128, 111, + 115, 113, 121, 124, 126, 107, 110, 112, 118, 116, + 131, 122, 240, 12, 254, 136, 250, 253, 0, 130, + 140, 257, 16, 252, 138, 137, 0, 256, 139, 259, + 231, 258, 255, 0, 0, 264, 0, 247, 246, 0, + 249, 248, 245, 241, 99, 263, 0, 236, 0, 0, + 260, 97, 98, 101, 0, 132, 134, 133, 135, 0, + 0, 261, 0, 0, 176, 0, 173, 165, 167, 168, + 142, 154, 171, 162, 156, 157, 153, 159, 163, 161, + 169, 172, 152, 155, 158, 160, 166, 164, 174, 170, + 150, 175, 0, 144, 148, 146, 151, 141, 149, 0, + 147, 143, 145, 0, 15, 14, 262, 0, 22, 21, + 261, 30, 0, 20, 0, 0, 32, 37, 31, 0, + 33, 261, 0, 34, 0, 24, 0, 35, 0, 26, + 36, 25, 0, 242, 41, 40, 261, 43, 49, 261, + 42, 0, 44, 261, 49, 261, 0, 261, 0, 49, + 0, 48, 46, 47, 51, 52, 261, 261, 0, 57, + 261, 54, 261, 0, 58, 0, 55, 261, 53, 261, + 0, 56, 65, 0, 261, 61, 261, 0, 59, 62, + 63, 0, 261, 0, 0, 60, 64, 45, 50, 66, + 0, 39, 0, 0, 261, 0, 94, 95, 0, 0, + 0, 0, 261, 0, 210, 201, 203, 205, 178, 190, + 208, 199, 193, 191, 194, 189, 196, 198, 206, 209, + 188, 192, 195, 197, 202, 200, 204, 207, 211, 213, + 212, 186, 0, 0, 243, 180, 184, 182, 0, 0, + 93, 187, 177, 185, 0, 183, 179, 181, 92, 0, + 96, 0, 0, 0, 0, 0, 261, 86, 261, 0, + 262, 0, 87, 0, 89, 69, 74, 73, 70, 71, + 72, 261, 75, 76, 0, 0, 0, 269, 268, 266, + 267, 265, 67, 261, 0, 261, 0, 0, 68, 77, + 261, 0, 261, 0, 0, 78, 0, 79, 0, 82, + 85, 0, 0, 215, 225, 224, 0, 227, 229, 228, + 226, 0, 244, 217, 221, 219, 223, 214, 222, 0, + 220, 216, 218, 0, 81, 80, 0, 83, 0, 84, + 88, 100, 0, 38, 0, 0, 0, 0, 91, 90, + 0, 103, 23, 27, 29, 28, 0, 0, 261, 262, + 0, 261, 0, 106, 105, 261, 0, 104, 102, 0, + 0, 18, 261, 17, 0, 19, 0, 0, 251, 0, + 261, 0, 239, 0, 232, 238, 0, 237, 234, 261, + 261, 262, 233, 235, 0, 261, 0, 230, 261, 0, + 261, 0, 231, 0, 0, 13, 270, 9, 5, 8, + 4, 0, 7, 259, 6, 0, 3}; + +const short QXmlStreamReader_Table::goto_default [] = { + 2, 4, 3, 49, 388, 43, 37, 52, 47, 41, + 249, 53, 127, 84, 393, 81, 85, 126, 42, 46, + 169, 130, 131, 146, 145, 149, 138, 136, 140, 147, + 139, 159, 160, 157, 168, 167, 209, 165, 164, 166, + 187, 180, 196, 200, 303, 302, 295, 321, 320, 319, + 279, 277, 278, 142, 56, 141, 222, 38, 34, 148, + 39, 48, 40, 248, 45, 36, 119, 112, 330, 111, + 264, 252, 251, 250, 339, 326, 325, 329, 398, 399, + 50, 51, 59, 0}; + +const short QXmlStreamReader_Table::action_index [] = { + -21, -57, 33, 119, 960, 70, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, 105, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, 40, -57, + 795, -57, 47, -57, -57, -57, 107, -57, -57, -57, + 84, -57, -57, -38, 80, -57, 12, -57, -57, 97, + -57, -57, -57, -57, -57, -57, 13, -57, 56, 34, + -57, -57, -57, -57, 51, -57, -57, -57, -57, 53, + 57, 84, 300, 255, -57, 84, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, 355, -57, -57, -57, -57, -57, -57, 326, + -57, -57, -57, 48, -57, -57, -57, 50, -57, -57, + 84, 155, 32, -57, 38, 22, -57, -57, -57, 115, + -57, 35, 156, -57, 173, -57, 245, -57, 44, -57, + -57, -57, 16, -57, -57, -57, 29, -57, 116, 29, + -57, 133, -57, 29, 129, 84, 15, 29, -22, 121, + 74, -57, -57, -57, -57, 82, 29, 29, 88, -57, + 29, 7, 29, 86, -57, 83, -57, 27, 19, 26, + 94, -57, -57, 106, 29, 3, 29, -8, -57, -57, + -57, 104, 29, -6, -7, -57, -57, -57, -57, -57, + 17, -57, -2, 11, 29, 18, -57, -57, 850, 65, + 465, 67, 84, 135, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, 630, 24, -57, -57, -57, -57, 84, 76, + -57, -57, -57, -57, 740, -57, -57, -57, -57, 39, + -57, 23, 21, 14, 78, 22, 84, -57, 84, 184, + 20, 31, -57, 41, -57, -57, -57, -57, -57, -57, + -57, 84, -57, -57, 36, 126, 162, -57, -57, -57, + -57, -57, -57, 29, 79, 29, 29, 160, -57, -57, + 29, 145, 29, 75, 29, -57, 575, -57, 410, -57, + -57, 110, 64, -57, -57, -57, 685, -57, -57, -57, + -57, -17, -57, -57, -57, -57, -57, -57, -57, 520, + -57, -57, -57, 29, -57, -57, 61, -57, 29, -57, + -57, -57, 29, -57, 29, 29, -15, 29, -57, -57, + 29, -57, -57, -57, -57, -57, 95, 43, 29, 45, + 9, 29, 10, -57, -57, 29, 2, -57, -57, -24, + 190, -57, 29, -57, 1, -57, 905, 150, -57, -26, + 29, 0, -57, 109, -26, -57, 8, -57, -57, 29, + 29, -19, -57, -57, -11, 29, 59, -57, 29, -5, + 29, 103, 29, -16, 6, -57, -57, -57, -57, -57, + -57, 69, -57, -57, -57, 905, -57, + + -84, -84, -84, 204, 75, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, 7, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + 101, -84, -84, -84, -84, -84, -84, -84, -84, 64, + 54, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, 68, -84, 30, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + 32, -84, -16, -7, -84, 42, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, 45, -84, -84, -84, -84, -84, -84, 44, + -84, -84, -84, 33, -84, -84, -84, -84, -84, -84, + 36, 108, -84, -84, -84, 69, -84, -84, -84, 62, + -84, 63, -84, -84, -84, -84, 118, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -2, -84, -84, -10, + -84, -84, -84, 25, -21, 11, -84, 20, -84, -25, + -84, -84, -84, -84, -84, -84, 1, 2, -36, -84, + -9, -84, 5, -13, -84, -8, -84, 6, -84, 8, + 12, -84, -84, -84, 23, -84, 4, -1, -84, -84, + -84, -84, 0, -84, -14, -84, -84, -84, -84, -84, + -84, -84, 55, -84, 58, -84, -84, -84, -84, 53, + 47, 123, 67, 66, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -15, -84, -84, -84, -84, -84, 41, 40, + -84, -84, -84, -84, -46, -84, -84, -84, -84, -84, + -84, 35, -84, 34, 37, 18, 70, -84, 89, -84, + 43, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, 48, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, 31, -84, 29, 27, 17, -84, -84, + 38, 24, 39, -84, 49, -84, 71, -84, 93, -84, + -84, -84, -12, -84, -84, -84, 94, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, 78, + -84, -84, -84, 50, -84, -84, 46, -84, 56, -84, + -84, -84, 60, -84, 61, 59, 51, 57, -84, -84, + 14, -84, -84, -84, -84, -84, -11, -6, 72, -5, + -84, -3, -84, -84, -84, 52, -84, -84, -84, -20, + 77, -84, 21, -84, -84, -84, 76, 16, -84, 19, + 26, -84, -84, -84, 10, -84, -84, -84, -84, 80, + 13, 73, -84, -84, -84, 22, -27, -84, 9, -84, + 28, 15, 82, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, 3, -84, 98, -84}; + +const short QXmlStreamReader_Table::action_info [] = { + 65, 332, 65, 405, 392, 385, 377, 65, 414, 410, + 415, 55, 397, 374, 373, 217, 206, 408, 65, 65, + 207, 211, 216, 1, 55, 199, 182, 192, 70, 70, + 63, 70, 189, 416, 153, 350, 133, 70, 72, 55, + 65, 351, 254, 270, 73, 284, 65, 310, 55, 65, + 83, 82, 83, 82, 129, 83, 82, 54, 70, 128, + 83, 82, 66, 64, 83, 82, 318, 316, 318, 316, + 54, 212, 83, 82, 83, 82, 54, 55, 367, 366, + 69, 80, 79, 83, 82, 163, 70, 314, 305, 272, + 55, 306, 305, 354, 163, 177, 55, 163, 379, 163, + 65, 176, 83, 82, 55, 163, 58, 57, 0, 65, + 83, 82, 65, 395, 65, 62, 203, 202, 195, 194, + 65, 417, 16, 61, 60, 396, 156, 272, 0, 66, + 64, 65, 317, 318, 316, 378, 379, 171, 173, 162, + 172, 54, 171, 173, 163, 172, 0, 345, 344, 343, + 171, 173, 0, 172, 0, 155, 154, 70, 134, 65, + 0, 55, 297, 220, 218, 298, 389, 0, 300, 0, + 135, 301, 299, 33, 66, 64, 65, 297, 0, 297, + 298, 0, 298, 300, 0, 300, 301, 299, 301, 299, + 221, 219, 70, 272, 381, 291, 0, 0, 0, 128, + 13, 0, 0, 273, 271, 274, 275, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 287, 294, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 285, 288, 289, 290, 286, 292, 293, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 70, 134, 0, + 0, 0, 0, 0, 0, 362, 0, 108, 0, 103, + 135, 94, 117, 116, 95, 104, 97, 105, 99, 93, + 98, 107, 87, 106, 88, 89, 100, 109, 92, 101, + 86, 96, 91, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 108, 0, 103, 0, 94, 102, 90, 95, + 104, 97, 105, 99, 93, 98, 107, 87, 106, 88, + 89, 100, 109, 92, 101, 86, 96, 91, 108, 0, + 103, 0, 94, 121, 120, 95, 104, 97, 105, 99, + 93, 98, 107, 87, 106, 88, 89, 100, 109, 92, + 101, 86, 96, 91, 0, 0, 0, 108, 0, 103, + 0, 94, 114, 113, 95, 104, 97, 105, 99, 93, + 98, 107, 87, 106, 88, 89, 100, 109, 92, 101, + 86, 96, 91, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 108, 0, 103, 322, 94, 337, 336, 95, + 104, 97, 105, 99, 93, 98, 107, 87, 106, 88, + 89, 100, 109, 92, 101, 86, 96, 91, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 246, 233, 241, + 223, 232, 262, 261, 234, 242, 236, 243, 237, 231, + 0, 245, 225, 244, 226, 227, 238, 247, 230, 239, + 224, 235, 229, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 108, 0, 103, 322, 94, 341, 340, 95, + 104, 97, 105, 99, 93, 98, 107, 87, 106, 88, + 89, 100, 109, 92, 101, 86, 96, 91, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 108, 0, 103, + 322, 94, 324, 323, 95, 104, 97, 105, 99, 93, + 98, 107, 87, 106, 88, 89, 100, 109, 92, 101, + 86, 96, 91, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 246, 233, 241, 223, 232, 256, 255, 234, + 242, 236, 243, 237, 231, 0, 245, 225, 244, 226, + 227, 238, 247, 230, 239, 224, 235, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 108, 0, 103, + 322, 94, 334, 333, 95, 104, 97, 105, 99, 93, + 98, 107, 87, 106, 88, 89, 100, 109, 92, 101, + 86, 96, 91, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 246, 233, 241, 223, 232, 266, 265, 234, + 242, 236, 243, 237, 231, 0, 245, 225, 244, 226, + 227, 238, 247, 230, 239, 224, 235, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 0, 25, + 74, 15, 24, 10, 17, 26, 19, 27, 21, 14, + 20, 29, 7, 28, 8, 9, 22, 31, 12, 23, + 6, 18, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 246, 233, 241, 223, 232, 240, 228, 234, + 242, 236, 243, 237, 231, 0, 245, 225, 244, 226, + 227, 238, 247, 230, 239, 224, 235, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 387, 25, + 5, 15, 24, 10, 17, 26, 19, 27, 21, 14, + 20, 29, 7, 28, 8, 9, 22, 31, 12, 23, + 6, 18, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 32, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 30, 16, 25, 5, 15, 24, 10, 17, + 26, 19, 27, 21, 14, 20, 29, 7, 28, 8, + 9, 22, 31, 12, 23, 6, 18, 11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 32, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, + + 380, 179, 210, 181, 425, 368, 205, 375, 371, 372, + 161, 208, 204, 178, 185, 174, 201, 183, 188, 198, + 190, 409, 407, 175, 184, 404, 267, 67, 412, 186, + 400, 361, 193, 384, 406, 197, 67, 170, 391, 390, + 411, 307, 331, 304, 309, 125, 124, 71, 132, 191, + 311, 313, 110, 260, 352, 276, 0, 257, 259, 123, + 296, 118, 308, 348, 376, 386, 315, 346, 312, 258, + 215, 394, 360, 349, 358, 213, 359, 353, 356, 269, + 0, 328, 281, 0, 370, 44, 44, 280, 328, 369, + 0, 355, 402, 400, 383, 347, 413, 401, 382, 394, + 158, 283, 426, 328, 328, 357, 280, 0, 44, 214, + 0, 76, 122, 115, 137, 0, 150, 0, 143, 263, + 253, 0, 68, 152, 137, 151, 150, 144, 143, 0, + 0, 0, 0, 0, 327, 365, 268, 144, 35, 35, + 282, 327, 363, 364, 0, 0, 0, 0, 0, 0, + 0, 403, 0, 0, 342, 0, 327, 327, 0, 0, + 0, 35, 78, 0, 75, 77, 0, 0, 0, 338, + 335, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 423, 0, 420, + 418, 424, 422, 419, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 421, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + +const short QXmlStreamReader_Table::action_check [] = { + 26, 18, 26, 14, 4, 4, 4, 26, 24, 14, + 4, 26, 4, 4, 4, 4, 22, 55, 26, 26, + 42, 4, 4, 44, 26, 22, 19, 12, 2, 2, + 18, 2, 13, 0, 18, 4, 4, 2, 4, 26, + 26, 20, 18, 4, 4, 4, 26, 11, 26, 26, + 7, 8, 7, 8, 4, 7, 8, 6, 2, 9, + 7, 8, 24, 25, 7, 8, 7, 8, 7, 8, + 6, 36, 7, 8, 7, 8, 6, 26, 34, 35, + 24, 34, 35, 7, 8, 11, 2, 12, 13, 20, + 26, 12, 13, 15, 11, 13, 26, 11, 29, 11, + 26, 19, 7, 8, 26, 11, 26, 27, -1, 26, + 7, 8, 26, 4, 26, 18, 12, 13, 12, 13, + 26, 2, 3, 26, 27, 16, 11, 20, -1, 24, + 25, 26, 6, 7, 8, 28, 29, 21, 22, 6, + 24, 6, 21, 22, 11, 24, -1, 37, 38, 39, + 21, 22, -1, 24, -1, 40, 41, 2, 3, 26, + -1, 26, 17, 7, 8, 20, 16, -1, 23, -1, + 15, 26, 27, 54, 24, 25, 26, 17, -1, 17, + 20, -1, 20, 23, -1, 23, 26, 27, 26, 27, + 34, 35, 2, 20, 4, 11, -1, -1, -1, 9, + 45, -1, -1, 30, 31, 32, 33, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 32, 33, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 47, 48, 49, 50, 51, 52, 53, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, 3, -1, + -1, -1, -1, -1, -1, 10, -1, 2, -1, 4, + 15, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2, -1, 4, -1, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 2, -1, + 4, -1, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, -1, -1, -1, 2, -1, 4, + -1, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2, -1, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + -1, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2, -1, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, -1, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, -1, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, -1, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, -1, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, -1, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, -1, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 45, 46, -1, -1, -1, -1, -1, -1, -1, 54, + -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 45, 46, -1, -1, -1, + -1, -1, -1, -1, 54, -1, -1, + + 20, 37, 12, 12, 1, 16, 20, 13, 13, 12, + 12, 36, 12, 12, 12, 36, 12, 12, 12, 20, + 12, 12, 49, 12, 37, 12, 72, 20, 13, 37, + 20, 17, 12, 12, 12, 12, 20, 12, 12, 20, + 12, 12, 54, 12, 17, 13, 13, 17, 12, 37, + 12, 12, 68, 13, 20, 20, -1, 72, 17, 17, + 12, 68, 45, 20, 12, 1, 17, 17, 44, 16, + 12, 17, 54, 17, 17, 12, 17, 17, 17, 12, + -1, 10, 12, -1, 12, 10, 10, 17, 10, 17, + -1, 54, 12, 20, 17, 49, 14, 17, 21, 17, + 38, 12, 4, 10, 10, 54, 17, -1, 10, 54, + -1, 10, 68, 68, 6, -1, 8, -1, 10, 72, + 54, -1, 54, 54, 6, 17, 8, 19, 10, -1, + -1, -1, -1, -1, 63, 17, 13, 19, 63, 63, + 51, 63, 24, 25, -1, -1, -1, -1, -1, -1, + -1, 78, -1, -1, 76, -1, 63, 63, -1, -1, + -1, 63, 61, -1, 63, 64, -1, -1, -1, 76, + 76, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 3, -1, 5, + 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 19, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}; + + +template <typename T> class QXmlStreamSimpleStack { + T *data; + int tos, cap; +public: + inline QXmlStreamSimpleStack():data(0), tos(-1), cap(0){} + inline ~QXmlStreamSimpleStack(){ if (data) free(data); } + + inline void reserve(int extraCapacity) { + if (tos + extraCapacity + 1 > cap) { + cap = qMax(tos + extraCapacity + 1, cap << 1 ); + data = reinterpret_cast<T *>(realloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); + } + } + + inline T &push() { reserve(1); return data[++tos]; } + inline T &rawPush() { return data[++tos]; } + inline const T &top() const { return data[tos]; } + inline T &top() { return data[tos]; } + inline T &pop() { return data[tos--]; } + inline T &operator[](int index) { return data[index]; } + inline const T &at(int index) const { return data[index]; } + inline int size() const { return tos + 1; } + inline void resize(int s) { tos = s - 1; } + inline bool isEmpty() const { return tos < 0; } + inline void clear() { tos = -1; } +}; + + +class QXmlStream +{ + Q_DECLARE_TR_FUNCTIONS(QXmlStream) +}; + +class QXmlStreamPrivateTagStack { +public: + struct NamespaceDeclaration + { + QStringRef prefix; + QStringRef namespaceUri; + }; + + struct Tag + { + QStringRef name; + QStringRef qualifiedName; + NamespaceDeclaration namespaceDeclaration; + int tagStackStringStorageSize; + int namespaceDeclarationsSize; + }; + + + QXmlStreamPrivateTagStack(); + QXmlStreamSimpleStack<NamespaceDeclaration> namespaceDeclarations; + QString tagStackStringStorage; + int tagStackStringStorageSize; + int initialTagStackStringStorageSize; + bool tagsDone; + + inline QStringRef addToStringStorage(const QStringRef &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + inline QStringRef addToStringStorage(const QString &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + QStringRef addToStringStorage(QStringView s) + { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.append(s.data(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + + QXmlStreamSimpleStack<Tag> tagStack; + + + inline Tag &tagStack_pop() { + Tag& tag = tagStack.pop(); + tagStackStringStorageSize = tag.tagStackStringStorageSize; + namespaceDeclarations.resize(tag.namespaceDeclarationsSize); + tagsDone = tagStack.isEmpty(); + return tag; + } + inline Tag &tagStack_push() { + Tag &tag = tagStack.push(); + tag.tagStackStringStorageSize = tagStackStringStorageSize; + tag.namespaceDeclarationsSize = namespaceDeclarations.size(); + return tag; + } +}; + + +class QXmlStreamEntityResolver; +#ifndef QT_NO_XMLSTREAMREADER +class QXmlStreamReaderPrivate : public QXmlStreamReader_Table, public QXmlStreamPrivateTagStack{ + QXmlStreamReader *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamReader) +public: + QXmlStreamReaderPrivate(QXmlStreamReader *q); + ~QXmlStreamReaderPrivate(); + void init(); + + QByteArray rawReadBuffer; + QByteArray dataBuffer; + uchar firstByte; + qint64 nbytesread; + QString readBuffer; + int readBufferPos; + QXmlStreamSimpleStack<uint> putStack; + struct Entity { + Entity() = default; + Entity(const QString &name, const QString &value) + : name(name), value(value), external(false), unparsed(false), literal(false), + hasBeenParsed(false), isCurrentlyReferenced(false){} + static inline Entity createLiteral(QLatin1String name, QLatin1String value) + { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; } + QString name, value; + uint external : 1; + uint unparsed : 1; + uint literal : 1; + uint hasBeenParsed : 1; + uint isCurrentlyReferenced : 1; + }; + // these hash tables use a QStringView as a key to avoid creating QStrings + // just for lookup. The keys are usually views into Entity::name and thus + // are guaranteed to have the same lifetime as the referenced data: + QHash<QStringView, Entity> entityHash; + QHash<QStringView, Entity> parameterEntityHash; + QXmlStreamSimpleStack<Entity *>entityReferenceStack; + inline bool referenceEntity(Entity &entity) { + if (entity.isCurrentlyReferenced) { + raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + return false; + } + entity.isCurrentlyReferenced = true; + entityReferenceStack.push() = &entity; + injectToken(ENTITY_DONE); + return true; + } + + + QIODevice *device; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextDecoder *decoder; +#endif + bool atEnd; + + /*! + \sa setType() + */ + QXmlStreamReader::TokenType type; + QXmlStreamReader::Error error; + QString errorString; + QString unresolvedEntity; + + qint64 lineNumber, lastLineStart, characterOffset; + + + void write(const QString &); + void write(const char *); + + + QXmlStreamAttributes attributes; + QStringRef namespaceForPrefix(const QStringRef &prefix); + void resolveTag(); + void resolvePublicNamespaces(); + void resolveDtd(); + uint resolveCharRef(int symbolIndex); + bool checkStartDocument(); + void startDocument(); + void parseError(); + void checkPublicLiteral(const QStringRef &publicId); + + bool scanDtd; + QStringRef lastAttributeValue; + bool lastAttributeIsCData; + struct DtdAttribute { + QStringRef tagName; + QStringRef attributeQualifiedName; + QStringRef attributePrefix; + QStringRef attributeName; + QStringRef defaultValue; + bool isCDATA; + bool isNamespaceAttribute; + }; + QXmlStreamSimpleStack<DtdAttribute> dtdAttributes; + struct NotationDeclaration { + QStringRef name; + QStringRef publicId; + QStringRef systemId; + }; + QXmlStreamSimpleStack<NotationDeclaration> notationDeclarations; + QXmlStreamNotationDeclarations publicNotationDeclarations; + QXmlStreamNamespaceDeclarations publicNamespaceDeclarations; + + struct EntityDeclaration { + QStringRef name; + QStringRef notationName; + QStringRef publicId; + QStringRef systemId; + QStringRef value; + bool parameter; + bool external; + inline void clear() { + name.clear(); + notationName.clear(); + publicId.clear(); + systemId.clear(); + value.clear(); + parameter = external = false; + } + }; + QXmlStreamSimpleStack<EntityDeclaration> entityDeclarations; + QXmlStreamEntityDeclarations publicEntityDeclarations; + + QStringRef text; + + QStringRef prefix, namespaceUri, qualifiedName, name; + QStringRef processingInstructionTarget, processingInstructionData; + QStringRef dtdName, dtdPublicId, dtdSystemId; + QStringRef documentVersion, documentEncoding; + uint isEmptyElement : 1; + uint isWhitespace : 1; + uint isCDATA : 1; + uint standalone : 1; + uint hasCheckedStartDocument : 1; + uint normalizeLiterals : 1; + uint hasSeenTag : 1; + uint inParseEntity : 1; + uint referenceToUnparsedEntityDetected : 1; + uint referenceToParameterEntityDetected : 1; + uint hasExternalDtdSubset : 1; + uint lockEncoding : 1; + uint namespaceProcessing : 1; + + int resumeReduction; + void resume(int rule); + + inline bool entitiesMustBeDeclared() const { + return (!inParseEntity + && (standalone + || (!referenceToUnparsedEntityDetected + && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 + && !hasExternalDtdSubset))); + } + + // qlalr parser + int tos; + int stack_size; + struct Value { + int pos; + int len; + int prefix; + ushort c; + }; + + Value *sym_stack; + int *state_stack; + inline void reallocateStack(); + inline Value &sym(int index) const + { return sym_stack[tos + index - 1]; } + QString textBuffer; + inline void clearTextBuffer() { + if (!scanDtd) { + textBuffer.resize(0); + textBuffer.reserve(256); + } + } + struct Attribute { + Value key; + Value value; + }; + QXmlStreamSimpleStack<Attribute> attributeStack; + + inline QStringRef symString(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + QStringView symView(int index) const + { + const Value &symbol = sym(index); + return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix); + } + inline QStringRef symName(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symString(int index, int offset) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); + } + inline QStringRef symPrefix(int index) { + const Value &symbol = sym(index); + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + inline QStringRef symString(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symPrefix(const Value &symbol) { + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + + inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } + + + short token; + uint token_char; + + uint filterCarriageReturn(); + inline uint getChar(); + inline uint peekChar(); + inline void putChar(uint c) { putStack.push() = c; } + inline void putChar(QChar c) { putStack.push() = c.unicode(); } + void putString(const QString &s, int from = 0); + void putStringLiteral(const QString &s); + void putReplacement(const QString &s); + void putReplacementInAttributeValue(const QString &s); + uint getChar_helper(); + + bool scanUntil(const char *str, short tokenToInject = -1); + bool scanString(const char *str, short tokenToInject, bool requireSpace = true); + inline void injectToken(ushort tokenToInject) { + putChar(int(tokenToInject) << 16); + } + + QString resolveUndeclaredEntity(const QString &name); + void parseEntity(const QString &value); + QXmlStreamReaderPrivate *entityParser; + + bool scanAfterLangleBang(); + bool scanPublicOrSystem(); + bool scanNData(); + bool scanAfterDefaultDecl(); + bool scanAttType(); + + + // scan optimization functions. Not strictly necessary but LALR is + // not very well suited for scanning fast + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); + int fastScanName(int *prefix = 0); + inline int fastScanNMTOKEN(); + + + bool parse(); + inline void consumeRule(int); + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); + + QXmlStreamEntityResolver *entityResolver; + +private: + /*! \internal + Never assign to variable type directly. Instead use this function. + + This prevents errors from being ignored. + */ + inline void setType(const QXmlStreamReader::TokenType t) + { + if(type != QXmlStreamReader::Invalid) + type = t; + } +}; + +bool QXmlStreamReaderPrivate::parse() +{ + // cleanup currently reported token + + switch (type) { + case QXmlStreamReader::StartElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + publicNamespaceDeclarations.clear(); + attributes.clear(); + if (isEmptyElement) { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + isEmptyElement = false; + return true; + } + clearTextBuffer(); + break; + case QXmlStreamReader::EndElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::DTD: + publicNotationDeclarations.clear(); + publicEntityDeclarations.clear(); + dtdName.clear(); + dtdPublicId.clear(); + dtdSystemId.clear(); + Q_FALLTHROUGH(); + case QXmlStreamReader::Comment: + case QXmlStreamReader::Characters: + isCDATA = false; + isWhitespace = true; + text.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::EntityReference: + text.clear(); + name.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::ProcessingInstruction: + processingInstructionTarget.clear(); + processingInstructionData.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + lockEncoding = true; + documentVersion.clear(); + documentEncoding.clear(); +#ifndef QT_NO_TEXTCODEC + if (decoder && decoder->hasFailure()) { + raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return false; + } +#endif + Q_FALLTHROUGH(); + default: + clearTextBuffer(); + ; + } + + setType(QXmlStreamReader::NoToken); + + + // the main parse loop + int act, r; + + if (resumeReduction) { + act = state_stack[tos-1]; + r = resumeReduction; + resumeReduction = 0; + goto ResumeReduction; + } + + act = state_stack[tos]; + + forever { + if (token == -1 && - TERMINAL_COUNT != action_index[act]) { + uint cu = getChar(); + token = NOTOKEN; + token_char = cu == ~0U ? cu : ushort(cu); + if ((cu != ~0U) && (cu & 0xff0000)) { + token = cu >> 16; + } else switch (token_char) { + case 0xfffe: + case 0xffff: + token = ERROR; + break; + case '\r': + token = SPACE; + if (cu == '\r') { + if ((token_char = filterCarriageReturn())) { + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + break; + } + } else { + break; + } + Q_FALLTHROUGH(); + case ~0U: { + token = EOF_SYMBOL; + if (!tagsDone && !inParseEntity) { + int a = t_action(act, token); + if (a < 0) { + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + return false; + } + } + + } break; + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + token = SPACE; + break; + case '&': + token = AMPERSAND; + break; + case '#': + token = HASH; + break; + case '\'': + token = QUOTE; + break; + case '\"': + token = DBLQUOTE; + break; + case '<': + token = LANGLE; + break; + case '>': + token = RANGLE; + break; + case '[': + token = LBRACK; + break; + case ']': + token = RBRACK; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '|': + token = PIPE; + break; + case '=': + token = EQ; + break; + case '%': + token = PERCENT; + break; + case '/': + token = SLASH; + break; + case ':': + token = COLON; + break; + case ';': + token = SEMICOLON; + break; + case ',': + token = COMMA; + break; + case '-': + token = DASH; + break; + case '+': + token = PLUS; + break; + case '*': + token = STAR; + break; + case '.': + token = DOT; + break; + case '?': + token = QUESTIONMARK; + break; + case '!': + token = BANG; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token = DIGIT; + break; + default: + if (cu < 0x20) + token = NOTOKEN; + else + token = LETTER; + break; + } + } + + act = t_action (act, token); + if (act == ACCEPT_STATE) { + // reset the parser in case someone resumes (process instructions can follow a valid document) + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return true; + } else if (act > 0) { + if (++tos == stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; + val.c = token_char; + val.pos = textBuffer.size(); + val.prefix = 0; + val.len = 1; + if (token_char) + textBuffer += QChar(token_char); + + state_stack[tos] = act; + token = -1; + + + } else if (act < 0) { + r = - act - 1; + +#if defined (QLALR_DEBUG) + int ridx = rule_index[r]; + printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); + ++ridx; + for (int i = ridx; i < ridx + rhs[r]; ++i) { + int symbol = rule_info[i]; + if (const char *name = spell[symbol]) + printf (" %s", name); + else + printf (" #%d", symbol); + } + printf ("\n"); +#endif + + tos -= rhs[r]; + act = state_stack[tos++]; + ResumeReduction: + switch (r) { + + case 0: + setType(QXmlStreamReader::EndDocument); + break; + + case 1: + if (type != QXmlStreamReader::Invalid) { + if (hasSeenTag || inParseEntity) { + setType(QXmlStreamReader::EndDocument); + } else { + raiseError(QXmlStreamReader::NotWellFormedError, QXmlStream::tr("Start tag expected.")); + // reset the parser + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return false; + } + } + break; + + case 10: + entityReferenceStack.pop()->isCurrentlyReferenced = false; + clearSym(); + break; + + case 11: + if (!scanString(spell[VERSION], VERSION, false) && atEnd) { + resume(11); + return false; + } + break; + + case 12: + setType(QXmlStreamReader::StartDocument); + documentVersion = symString(6); + startDocument(); + break; + + case 13: + hasExternalDtdSubset = true; + dtdSystemId = symString(2); + break; + + case 14: + checkPublicLiteral(symString(2)); + dtdPublicId = symString(2); + dtdSystemId = symString(4); + hasExternalDtdSubset = true; + break; + + case 16: + if (!scanPublicOrSystem() && atEnd) { + resume(16); + return false; + } + dtdName = symString(3); + break; + + case 17: + case 18: + dtdName = symString(3); + Q_FALLTHROUGH(); + + case 19: + case 20: + setType(QXmlStreamReader::DTD); + text = &textBuffer; + break; + + case 21: + scanDtd = true; + break; + + case 22: + scanDtd = false; + break; + + case 37: + if (!scanString(spell[EMPTY], EMPTY, false) + && !scanString(spell[ANY], ANY, false) + && atEnd) { + resume(37); + return false; + } + break; + + case 43: + if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { + resume(43); + return false; + } + break; + + case 68: { + lastAttributeIsCData = true; + } break; + + case 78: + if (!scanAfterDefaultDecl() && atEnd) { + resume(78); + return false; + } + break; + + case 83: + sym(1) = sym(2); + lastAttributeValue.clear(); + lastAttributeIsCData = false; + if (!scanAttType() && atEnd) { + resume(83); + return false; + } + break; + + case 84: { + DtdAttribute &dtdAttribute = dtdAttributes.push(); + dtdAttribute.tagName.clear(); + dtdAttribute.isCDATA = lastAttributeIsCData; + dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); + dtdAttribute.attributeName = addToStringStorage(symString(1)); + dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); + dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") + || (dtdAttribute.attributePrefix.isEmpty() + && dtdAttribute.attributeName == QLatin1String("xmlns"))); + if (lastAttributeValue.isNull()) { + dtdAttribute.defaultValue.clear(); + } else { + if (dtdAttribute.isCDATA) + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); + else + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); + + } + } break; + + case 88: { + if (referenceToUnparsedEntityDetected && !standalone) + break; + int n = dtdAttributes.size(); + QStringRef tagName = addToStringStorage(symName(3)); + while (n--) { + DtdAttribute &dtdAttribute = dtdAttributes[n]; + if (!dtdAttribute.tagName.isNull()) + break; + dtdAttribute.tagName = tagName; + for (int i = 0; i < n; ++i) { + if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) + && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { + dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it + break; + } + } + } + } break; + + case 89: { + if (!scanPublicOrSystem() && atEnd) { + resume(89); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(3); + } break; + + case 90: { + if (!scanPublicOrSystem() && atEnd) { + resume(90); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(5); + entityDeclaration.parameter = true; + } break; + + case 91: { + if (!scanNData() && atEnd) { + resume(91); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.systemId = symString(3); + entityDeclaration.external = true; + } break; + + case 92: { + if (!scanNData() && atEnd) { + resume(92); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + checkPublicLiteral((entityDeclaration.publicId = symString(3))); + entityDeclaration.systemId = symString(5); + entityDeclaration.external = true; + } break; + + case 93: { + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.notationName = symString(3); + if (entityDeclaration.parameter) + raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); + } + Q_FALLTHROUGH(); + + case 94: + case 95: { + if (referenceToUnparsedEntityDetected && !standalone) { + entityDeclarations.pop(); + break; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + if (!entityDeclaration.external) + entityDeclaration.value = symString(2); + auto &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; + if (!hash.contains(qToStringViewIgnoringNull(entityDeclaration.name))) { + Entity entity(entityDeclaration.name.toString(), + entityDeclaration.value.toString()); + entity.unparsed = (!entityDeclaration.notationName.isNull()); + entity.external = entityDeclaration.external; + hash.insert(qToStringViewIgnoringNull(entity.name), entity); + } + } break; + + case 96: { + setType(QXmlStreamReader::ProcessingInstruction); + int pos = sym(4).pos + sym(4).len; + processingInstructionTarget = symString(3); + if (scanUntil("?>")) { + processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { + raiseWellFormedError(QXmlStream::tr("XML declaration not at start of document.")); + } + else if (!QXmlUtils::isNCName(processingInstructionTarget)) + raiseWellFormedError(QXmlStream::tr("%1 is an invalid processing instruction name.") + .arg(processingInstructionTarget)); + } else if (type != QXmlStreamReader::Invalid){ + resume(96); + return false; + } + } break; + + case 97: + setType(QXmlStreamReader::ProcessingInstruction); + processingInstructionTarget = symString(3); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) + raiseWellFormedError(QXmlStream::tr("Invalid processing instruction name.")); + break; + + case 98: + if (!scanAfterLangleBang() && atEnd) { + resume(98); + return false; + } + break; + + case 99: + if (!scanUntil("--")) { + resume(99); + return false; + } + break; + + case 100: { + setType(QXmlStreamReader::Comment); + int pos = sym(1).pos + 4; + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } break; + + case 101: { + setType(QXmlStreamReader::Characters); + isCDATA = true; + isWhitespace = false; + int pos = sym(2).pos; + if (scanUntil("]]>", -1)) { + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } else { + resume(101); + return false; + } + } break; + + case 102: { + if (!scanPublicOrSystem() && atEnd) { + resume(102); + return false; + } + NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); + notationDeclaration.name = symString(3); + } break; + + case 103: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId = symString(3); + notationDeclaration.publicId.clear(); + } break; + + case 104: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId.clear(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + } break; + + case 105: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + notationDeclaration.systemId = symString(5); + } break; + + case 129: + isWhitespace = false; + Q_FALLTHROUGH(); + + case 130: + sym(1).len += fastScanContentCharList(); + if (atEnd && !inParseEntity) { + resume(130); + return false; + } + break; + + case 139: + if (!textBuffer.isEmpty()) { + setType(QXmlStreamReader::Characters); + text = &textBuffer; + } + break; + + case 140: + case 141: + clearSym(); + break; + + case 142: + case 143: + sym(1) = sym(2); + break; + + case 144: + case 145: + case 146: + case 147: + sym(1).len += sym(2).len; + break; + + case 173: + if (normalizeLiterals) + textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); + break; + + case 174: + sym(1).len += fastScanLiteralContent(); + if (atEnd) { + resume(174); + return false; + } + break; + + case 175: { + if (!QXmlUtils::isPublicID(symString(1))) { + raiseWellFormedError(QXmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1))); + resume(175); + return false; + } + } break; + + case 176: + case 177: + clearSym(); + break; + + case 178: + case 179: + sym(1) = sym(2); + break; + + case 180: + case 181: + case 182: + case 183: + sym(1).len += sym(2).len; + break; + + case 213: + case 214: + clearSym(); + break; + + case 215: + case 216: + sym(1) = sym(2); + lastAttributeValue = symString(1); + break; + + case 217: + case 218: + case 219: + case 220: + sym(1).len += sym(2).len; + break; + + case 229: { + QStringRef prefix = symPrefix(1); + if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(symString(5)); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = addToStringStorage(ns); + } else { + Attribute &attribute = attributeStack.push(); + attribute.key = sym(1); + attribute.value = sym(5); + + QStringRef attributeQualifiedName = symName(1); + bool normalize = false; + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isCDATA + && dtdAttribute.tagName == qualifiedName + && dtdAttribute.attributeQualifiedName == attributeQualifiedName + ) { + normalize = true; + break; + } + } + if (normalize) { + // normalize attribute value (simplify and trim) + int pos = textBuffer.size(); + int n = 0; + bool wasSpace = true; + for (int i = 0; i < attribute.value.len; ++i) { + QChar c = textBuffer.at(attribute.value.pos + i); + if (c.unicode() == ' ') { + if (wasSpace) + continue; + wasSpace = true; + } else { + wasSpace = false; + } + textBuffer += textBuffer.at(attribute.value.pos + i); + ++n; + } + if (wasSpace) + while (n && textBuffer.at(pos + n - 1).unicode() == ' ') + --n; + attribute.value.pos = pos; + attribute.value.len = n; + } + if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = symString(attribute.key); + QStringRef namespaceUri = symString(attribute.value); + attributeStack.pop(); + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + } + } + } break; + + case 235: { + normalizeLiterals = true; + Tag &tag = tagStack_push(); + prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); + name = tag.name = addToStringStorage(symString(2)); + qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); + if ((!prefix.isEmpty() && !QXmlUtils::isNCName(prefix)) || !QXmlUtils::isNCName(name)) + raiseWellFormedError(QXmlStream::tr("Invalid XML name.")); + } break; + + case 236: + isEmptyElement = true; + Q_FALLTHROUGH(); + + case 237: + setType(QXmlStreamReader::StartElement); + resolveTag(); + if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) + raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); + hasSeenTag = true; + break; + + case 238: { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + if (qualifiedName != symName(3)) + raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch.")); + } break; + + case 239: + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); + break; + } + setType(QXmlStreamReader::EntityReference); + name = &unresolvedEntity; + break; + + case 240: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed) { + raiseWellFormedError(QXmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); + } else { + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacement(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + + injectToken(UNRESOLVED_ENTITY); + unresolvedEntity = symString(2).toString(); + textBuffer.chop(2 + sym(2).len); + clearSym(); + + } break; + + case 241: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (parameterEntityHash.contains(reference)) { + referenceToParameterEntityDetected = true; + Entity &entity = parameterEntityHash[reference]; + if (entity.unparsed || entity.external) { + referenceToUnparsedEntityDetected = true; + } else { + if (referenceEntity(entity)) + putString(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + } else if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(symString(2))); + } + } break; + + case 242: + sym(1).len += sym(2).len + 1; + break; + + case 243: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed || entity.value.isNull()) { + raiseWellFormedError(QXmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); + break; + } + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacementInAttributeValue(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(reference)); + } + } break; + + case 244: { + if (uint s = resolveCharRef(3)) { + if (s >= 0xffff) + putStringLiteral(QString::fromUcs4(&s, 1)); + else + putChar((LETTER << 16) | s); + + textBuffer.chop(3 + sym(3).len); + clearSym(); + } else { + raiseWellFormedError(QXmlStream::tr("Invalid character reference.")); + } + } break; + + case 247: + case 248: + sym(1).len += sym(2).len; + break; + + case 259: + sym(1).len += fastScanSpace(); + if (atEnd) { + resume(259); + return false; + } + break; + + case 262: { + sym(1).len += fastScanName(&sym(1).prefix); + if (atEnd) { + resume(262); + return false; + } + } break; + + case 263: + sym(1).len += fastScanName(); + if (atEnd) { + resume(263); + return false; + } + break; + + case 264: + case 265: + case 266: + case 267: + case 268: + sym(1).len += fastScanNMTOKEN(); + if (atEnd) { + resume(268); + return false; + } + + break; + + default: + ; + } // switch + act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); + if (type != QXmlStreamReader::NoToken) + return true; + } else { + parseError(); + break; + } + } + return false; +} +#endif //QT_NO_XMLSTREAMREADER.xml + + +#endif // QXMLSTREAM_P_H + diff --git a/src/corelib/serialization/qxmlutils.cpp b/src/corelib/serialization/qxmlutils.cpp new file mode 100644 index 0000000000..01c84251fd --- /dev/null +++ b/src/corelib/serialization/qxmlutils.cpp @@ -0,0 +1,390 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qstring.h> + +#include "qxmlutils_p.h" + +QT_BEGIN_NAMESPACE + +/* TODO: + * - isNameChar() doesn't have to be public, it's only needed in + * qdom.cpp -- refactor fixedXmlName() to use isNCName() + * - A lot of functions can be inlined. + */ + +class QXmlCharRange +{ +public: + ushort min; + ushort max; +}; +typedef const QXmlCharRange *RangeIter; + +/*! + Performs a binary search between \a begin and \a end inclusive, to check whether \a + c is contained. Remember that the QXmlCharRange instances must be in numeric order. + */ +bool QXmlUtils::rangeContains(RangeIter begin, RangeIter end, const QChar c) +{ + const ushort cp(c.unicode()); + + // check the first two ranges "manually" as characters in that + // range are checked very often and we avoid the binary search below. + + if (cp <= begin->max) + return cp >= begin->min; + + ++begin; + + if (begin == end) + return false; + + if (cp <= begin->max) + return cp >= begin->min; + + while (begin != end) { + int delta = (end - begin) / 2; + RangeIter mid = begin + delta; + + if (mid->min > cp) + end = mid; + else if (mid->max < cp) + begin = mid; + else + return true; + + if (delta == 0) + break; + } + + return false; +} + +// [85] BaseChar ::= ... + +static const QXmlCharRange g_base_begin[] = +{ + {0x0041, 0x005A}, {0x0061, 0x007A}, {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x00FF}, + {0x0100, 0x0131}, {0x0134, 0x013E}, {0x0141, 0x0148}, {0x014A, 0x017E}, {0x0180, 0x01C3}, + {0x01CD, 0x01F0}, {0x01F4, 0x01F5}, {0x01FA, 0x0217}, {0x0250, 0x02A8}, {0x02BB, 0x02C1}, + {0x0386, 0x0386}, {0x0388, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x03A1}, {0x03A3, 0x03CE}, + {0x03D0, 0x03D6}, {0x03DA, 0x03DA}, {0x03DC, 0x03DC}, {0x03DE, 0x03DE}, {0x03E0, 0x03E0}, + {0x03E2, 0x03F3}, {0x0401, 0x040C}, {0x040E, 0x044F}, {0x0451, 0x045C}, {0x045E, 0x0481}, + {0x0490, 0x04C4}, {0x04C7, 0x04C8}, {0x04CB, 0x04CC}, {0x04D0, 0x04EB}, {0x04EE, 0x04F5}, + {0x04F8, 0x04F9}, {0x0531, 0x0556}, {0x0559, 0x0559}, {0x0561, 0x0586}, {0x05D0, 0x05EA}, + {0x05F0, 0x05F2}, {0x0621, 0x063A}, {0x0641, 0x064A}, {0x0671, 0x06B7}, {0x06BA, 0x06BE}, + {0x06C0, 0x06CE}, {0x06D0, 0x06D3}, {0x06D5, 0x06D5}, {0x06E5, 0x06E6}, {0x0905, 0x0939}, + {0x093D, 0x093D}, {0x0958, 0x0961}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, + {0x09F0, 0x09F1}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, + {0x0A72, 0x0A74}, {0x0A85, 0x0A8B}, {0x0A8D, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, + {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABD, 0x0ABD}, {0x0AE0, 0x0AE0}, + {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B36, 0x0B39}, {0x0B3D, 0x0B3D}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB5}, {0x0BB7, 0x0BB9}, {0x0C05, 0x0C0C}, + {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C33}, {0x0C35, 0x0C39}, {0x0C60, 0x0C61}, + {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D28}, + {0x0D2A, 0x0D39}, {0x0D60, 0x0D61}, {0x0E01, 0x0E2E}, {0x0E30, 0x0E30}, {0x0E32, 0x0E33}, + {0x0E40, 0x0E45}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, + {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, + {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EAE}, {0x0EB0, 0x0EB0}, {0x0EB2, 0x0EB3}, + {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0F40, 0x0F47}, {0x0F49, 0x0F69}, {0x10A0, 0x10C5}, + {0x10D0, 0x10F6}, {0x1100, 0x1100}, {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, + {0x110B, 0x110C}, {0x110E, 0x1112}, {0x113C, 0x113C}, {0x113E, 0x113E}, {0x1140, 0x1140}, + {0x114C, 0x114C}, {0x114E, 0x114E}, {0x1150, 0x1150}, {0x1154, 0x1155}, {0x1159, 0x1159}, + {0x115F, 0x1161}, {0x1163, 0x1163}, {0x1165, 0x1165}, {0x1167, 0x1167}, {0x1169, 0x1169}, + {0x116D, 0x116E}, {0x1172, 0x1173}, {0x1175, 0x1175}, {0x119E, 0x119E}, {0x11A8, 0x11A8}, + {0x11AB, 0x11AB}, {0x11AE, 0x11AF}, {0x11B7, 0x11B8}, {0x11BA, 0x11BA}, {0x11BC, 0x11C2}, + {0x11EB, 0x11EB}, {0x11F0, 0x11F0}, {0x11F9, 0x11F9}, {0x1E00, 0x1E9B}, {0x1EA0, 0x1EF9}, + {0x1F00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, + {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, + {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x2126, 0x2126}, + {0x212A, 0x212B}, {0x212E, 0x212E}, {0x2180, 0x2182}, {0x3041, 0x3094}, {0x30A1, 0x30FA}, + {0x3105, 0x312C}, {0xAC00, 0xD7A3} +}; +static const RangeIter g_base_end = g_base_begin + sizeof(g_base_begin) / sizeof(QXmlCharRange); + +static const QXmlCharRange g_ideographic_begin[] = +{ + {0x3007, 0x3007}, {0x3021, 0x3029}, {0x4E00, 0x9FA5} +}; +static const RangeIter g_ideographic_end = g_ideographic_begin + sizeof(g_ideographic_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isIdeographic(const QChar c) +{ + return rangeContains(g_ideographic_begin, g_ideographic_end, c); +} + +static const QXmlCharRange g_combining_begin[] = +{ + {0x0300, 0x0345}, {0x0360, 0x0361}, {0x0483, 0x0486}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C4}, {0x064B, 0x0652}, + {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DD, 0x06DF}, {0x06E0, 0x06E4}, {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, {0x0901, 0x0903}, {0x093C, 0x093C}, {0x093E, 0x094C}, {0x094D, 0x094D}, + {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0983}, {0x09BC, 0x09BC}, {0x09BE, 0x09BE}, + {0x09BF, 0x09BF}, {0x09C0, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, + {0x09E2, 0x09E3}, {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A3E}, {0x0A3F, 0x0A3F}, + {0x0A40, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, {0x0A81, 0x0A83}, + {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0B01, 0x0B03}, + {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B43}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, + {0x0B82, 0x0B83}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, + {0x0C01, 0x0C03}, {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0C82, 0x0C83}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, + {0x0D02, 0x0D03}, {0x0D3E, 0x0D43}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F3E, 0x0F3E}, {0x0F3F, 0x0F3F}, {0x0F71, 0x0F84}, {0x0F86, 0x0F8B}, + {0x0F90, 0x0F95}, {0x0F97, 0x0F97}, {0x0F99, 0x0FAD}, {0x0FB1, 0x0FB7}, {0x0FB9, 0x0FB9}, + {0x20D0, 0x20DC}, {0x20E1, 0x20E1}, {0x302A, 0x302F}, {0x3099, 0x3099}, {0x309A, 0x309A} +}; +static const RangeIter g_combining_end = g_combining_begin + sizeof(g_combining_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isCombiningChar(const QChar c) +{ + return rangeContains(g_combining_begin, g_combining_end, c); +} + +// [88] Digit ::= ... +static const QXmlCharRange g_digit_begin[] = +{ + {0x0030, 0x0039}, {0x0660, 0x0669}, {0x06F0, 0x06F9}, {0x0966, 0x096F}, {0x09E6, 0x09EF}, + {0x0A66, 0x0A6F}, {0x0AE6, 0x0AEF}, {0x0B66, 0x0B6F}, {0x0BE7, 0x0BEF}, {0x0C66, 0x0C6F}, + {0x0CE6, 0x0CEF}, {0x0D66, 0x0D6F}, {0x0E50, 0x0E59}, {0x0ED0, 0x0ED9}, {0x0F20, 0x0F29} +}; +static const RangeIter g_digit_end = g_digit_begin + sizeof(g_digit_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isDigit(const QChar c) +{ + return rangeContains(g_digit_begin, g_digit_end, c); +} + +// [89] Extender ::= ... +static const QXmlCharRange g_extender_begin[] = +{ + {0x00B7, 0x00B7}, {0x02D0, 0x02D0}, {0x02D1, 0x02D1}, {0x0387, 0x0387}, {0x0640, 0x0640}, + {0x0E46, 0x0E46}, {0x0EC6, 0x0EC6}, {0x3005, 0x3005}, {0x3031, 0x3035}, {0x309D, 0x309E}, + {0x30FC, 0x30FE} +}; +static const RangeIter g_extender_end = g_extender_begin + sizeof(g_extender_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isExtender(const QChar c) +{ + return rangeContains(g_extender_begin, g_extender_end, c); +} + +bool QXmlUtils::isBaseChar(const QChar c) +{ + return rangeContains(g_base_begin, g_base_end, c); +} + +/*! + \internal + + Determines whether \a encName is a valid instance of production [81]EncName in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-EncName}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [81] EncName} + */ +bool QXmlUtils::isEncName(QStringView encName) +{ + // Valid encoding names are given by "[A-Za-z][A-Za-z0-9._\\-]*" + if (encName.isEmpty()) + return false; + const auto first = encName.front().unicode(); + if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z'))) + return false; + for (QChar ch : encName.mid(1)) { + const auto cp = ch.unicode(); + if ((cp >= 'a' && cp <= 'z') + || (cp >= 'A' && cp <= 'Z') + || (cp >= '0' && cp <= '9') + || cp == '.' || cp == '_' || cp == '-') { + continue; + } + return false; + } + return true; +} + +/*! + \internal + + Determines whether \a c is a valid instance of production [84]Letter in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-Letter}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [84] Letter} + */ +bool QXmlUtils::isLetter(const QChar c) +{ + return isBaseChar(c) || isIdeographic(c); +} + +/*! + \internal + + Determines whether \a c is a valid instance of production [2]Char in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-Char}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [2] Char} + */ +bool QXmlUtils::isChar(const QChar c) +{ + return (c.unicode() >= 0x0020 && c.unicode() <= 0xD7FF) + || c.unicode() == 0x0009 + || c.unicode() == 0x000A + || c.unicode() == 0x000D + || (c.unicode() >= 0xE000 && c.unicode() <= 0xFFFD); +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [4]NameChar in the XML 1.0 specification. If it + is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-NameChar}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [4] NameChar} + */ +bool QXmlUtils::isNameChar(const QChar c) +{ + return isBaseChar(c) + || isDigit(c) + || c.unicode() == '.' + || c.unicode() == '-' + || c.unicode() == '_' + || c.unicode() == ':' + || isCombiningChar(c) + || isIdeographic(c) + || isExtender(c); +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [12] PubidLiteral in the XML 1.0 specification. If it + is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-PubidLiteral}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [12] PubidLiteral} + */ +bool QXmlUtils::isPublicID(QStringView candidate) +{ + for (QChar ch : candidate) { + const ushort cp = ch.unicode(); + + if ((cp >= 'a' && cp <= 'z') + || (cp >= 'A' && cp <= 'Z') + || (cp >= '0' && cp <= '9')) + { + continue; + } + + switch (cp) + { + /* Fallthrough all these. */ + case 0x20: + case 0x0D: + case 0x0A: + case '-': + case '\'': + case '(': + case ')': + case '+': + case ',': + case '.': + case '/': + case ':': + case '=': + case '?': + case ';': + case '!': + case '*': + case '#': + case '@': + case '$': + case '_': + case '%': + continue; + default: + return false; + } + } + + return true; +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [4]NCName in the XML 1.0 Namespaces specification. If it + is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml-names/#NT-NCName}, + {W3CNamespaces in XML 1.0 (Second Edition), [4] NCName} + */ +bool QXmlUtils::isNCName(QStringView ncName) +{ + if(ncName.isEmpty()) + return false; + + const QChar first(ncName.at(0)); + + if(!QXmlUtils::isLetter(first) && first.unicode() != '_' && first.unicode() != ':') + return false; + + for (QChar at : ncName) { + if(!QXmlUtils::isNameChar(at) || at == QLatin1Char(':')) + return false; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qxmlutils_p.h b/src/corelib/serialization/qxmlutils_p.h new file mode 100644 index 0000000000..db6bddd5be --- /dev/null +++ b/src/corelib/serialization/qxmlutils_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXMLUTILS_P_H +#define QXMLUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class QString; +class QChar; +class QXmlCharRange; + +/*! + \internal + \short This class contains helper functions related to XML, for validating character classes, + productions in the XML specification, and so on. + */ +class Q_CORE_EXPORT QXmlUtils +{ +public: + static bool isEncName(QStringView encName); + static bool isChar(const QChar c); + static bool isNameChar(const QChar c); + static bool isLetter(const QChar c); + static bool isNCName(QStringView ncName); + static bool isPublicID(QStringView candidate); + +private: + typedef const QXmlCharRange *RangeIter; + static bool rangeContains(RangeIter begin, RangeIter end, const QChar c); + static bool isBaseChar(const QChar c); + static bool isDigit(const QChar c); + static bool isExtender(const QChar c); + static bool isIdeographic(const QChar c); + static bool isCombiningChar(const QChar c); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri new file mode 100644 index 0000000000..3d039dc30f --- /dev/null +++ b/src/corelib/serialization/serialization.pri @@ -0,0 +1,30 @@ +# Qt data formats core module + +HEADERS += \ + serialization/qdatastream.h \ + serialization/qdatastream_p.h \ + serialization/qjson_p.h \ + serialization/qjsondocument.h \ + serialization/qjsonobject.h \ + serialization/qjsonvalue.h \ + serialization/qjsonarray.h \ + serialization/qjsonwriter_p.h \ + serialization/qjsonparser_p.h \ + serialization/qtextstream.h \ + serialization/qtextstream_p.h \ + serialization/qxmlstream.h \ + serialization/qxmlstream_p.h \ + serialization/qxmlutils_p.h + +SOURCES += \ + serialization/qdatastream.cpp \ + serialization/qjson.cpp \ + serialization/qjsondocument.cpp \ + serialization/qjsonobject.cpp \ + serialization/qjsonarray.cpp \ + serialization/qjsonvalue.cpp \ + serialization/qjsonwriter.cpp \ + serialization/qjsonparser.cpp \ + serialization/qtextstream.cpp \ + serialization/qxmlstream.cpp \ + serialization/qxmlutils.cpp |