Merge remote-tracking branch 'origin/5.15' into dev

Change-Id: I379794a01cbf6fb39d94b24cc8c90b1971a212b9
This commit is contained in:
Qt Forward Merge Bot 2019-10-26 01:01:32 +02:00
commit 7b3bdcbfe8
113 changed files with 4177 additions and 3112 deletions

View File

@ -11,6 +11,12 @@ dita.metadata.default.audience = programmer
navigation.homepage = index.html navigation.homepage = index.html
navigation.hometitle = "Qt $QT_VER" navigation.hometitle = "Qt $QT_VER"
#Words to ignore for auto-linking
ignorewords += \
macOS \
WebChannel \
OpenGL
sourcedirs += includes $$BUILDDIR sourcedirs += includes $$BUILDDIR
url = http://doc.qt.io/qt-5 url = http://doc.qt.io/qt-5

View File

@ -70,8 +70,8 @@ QMAKE_CFLAGS_THREAD = -D_REENTRANT
QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden
QMAKE_CFLAGS_NEON = -mfpu=neon QMAKE_CFLAGS_NEON = -mfpu=neon
QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared QMAKE_LFLAGS_APP = -Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack -shared
QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared QMAKE_LFLAGS_SHLIB = -Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack -shared
QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB
QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined
QMAKE_LFLAGS_RPATH = -Wl,-rpath= QMAKE_LFLAGS_RPATH = -Wl,-rpath=

View File

@ -2,17 +2,25 @@ qtPrepareTool(MOC_COLLECT_JSON, moc)
QMAKE_MOC_OPTIONS += --output-json QMAKE_MOC_OPTIONS += --output-json
MOC_JSON_H_BASE = $${QMAKE_H_MOD_MOC}
MOC_JSON_CPP_BASE = $${QMAKE_CPP_MOD_MOC}
!isEmpty(MOC_DIR) {
MOC_JSON_H_BASE = $$MOC_DIR/$${MOC_JSON_H_BASE}
MOC_JSON_CPP_BASE = $$MOC_DIR/$${MOC_JSON_CPP_BASE}
}
moc_json_header.input = HEADERS moc_json_header.input = HEADERS
moc_json_header.output = $$MOC_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}.json moc_json_header.output = $${MOC_JSON_H_BASE}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}.json
moc_json_header.CONFIG = no_link moc_verify moc_json_header.CONFIG = no_link moc_verify
moc_json_header.depends = $$MOC_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)} moc_json_header.depends = $${MOC_JSON_H_BASE}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}
moc_json_header.commands = $$escape_expand(\\n) # force creation of rule moc_json_header.commands = $$escape_expand(\\n) # force creation of rule
moc_json_header.variable_out = MOC_JSON_FILES moc_json_header.variable_out = MOC_JSON_FILES
moc_json_source.input = SOURCES moc_json_source.input = SOURCES
moc_json_source.output = $$MOC_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC}.json moc_json_source.output = $${MOC_JSON_CPP_BASE}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC}.json
moc_json_source.CONFIG = no_link moc_verify moc_json_source.CONFIG = no_link moc_verify
moc_json_source.depends = $$MOC_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC} moc_json_source.depends = $${MOC_JSON_CPP_BASE}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC}
moc_json_source.commands = $$escape_expand(\\n) # force creation of rule moc_json_source.commands = $$escape_expand(\\n) # force creation of rule
moc_json_source.variable_out = MOC_JSON_FILES moc_json_source.variable_out = MOC_JSON_FILES

View File

@ -24,7 +24,7 @@ QOBJS = \
qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \ qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \
qfilesystementry.o qfsfileengine.o qfsfileengine_iterator.o \ qfilesystementry.o qfsfileengine.o qfsfileengine_iterator.o \
qiodevice.o qsettings.o qtemporaryfile.o qtextstream.o \ qiodevice.o qsettings.o qtemporaryfile.o qtextstream.o \
qjsonarray.o qjson.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \ qcborvalue.o qjsoncbor.o qjsonarray.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \
qmetatype.o qsystemerror.o qvariant.o \ qmetatype.o qsystemerror.o qvariant.o \
quuid.o \ quuid.o \
qarraydata.o qbitarray.o qbytearray.o qbytearraylist.o qbytearraymatcher.o \ qarraydata.o qbitarray.o qbytearray.o qbytearraylist.o qbytearraymatcher.o \
@ -96,9 +96,10 @@ DEPEND_SRC = \
$(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \
$(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp \
$(SOURCE_PATH)/src/corelib/plugin/quuid.cpp \ $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qcborvalue.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qjsonarray.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonarray.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qjson.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsoncbor.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qjsonobject.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonobject.cpp \
$(SOURCE_PATH)/src/corelib/serialization/qjsonparser.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonparser.cpp \
@ -466,7 +467,10 @@ qsystemlibrary.o: $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp
qdatastream.o: $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp qdatastream.o: $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $< $(CXX) -c -o $@ $(CXXFLAGS) $<
qjson.o: $(SOURCE_PATH)/src/corelib/serialization/qjson.cpp qcborvalue.o: $(SOURCE_PATH)/src/corelib/serialization/qcborvalue.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $<
qjsoncbor.o: $(SOURCE_PATH)/src/corelib/serialization/qjsoncbor.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $< $(CXX) -c -o $@ $(CXXFLAGS) $<
qjsondocument.o: $(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp qjsondocument.o: $(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp

View File

@ -118,7 +118,8 @@ QTOBJS= \
qxmlutils.obj \ qxmlutils.obj \
qnumeric.obj \ qnumeric.obj \
qlogging.obj \ qlogging.obj \
qjson.obj \ qcborvalue.obj \
qjsoncbor.obj \
qjsondocument.obj \ qjsondocument.obj \
qjsonparser.obj \ qjsonparser.obj \
qjsonarray.obj \ qjsonarray.obj \

View File

@ -4690,7 +4690,7 @@
The definitions above define a qmake target called \c mytarget, containing a The definitions above define a qmake target called \c mytarget, containing a
Makefile target called \c{.buildfile} which in turn is generated with the Makefile target called \c{.buildfile} which in turn is generated with the
\l{touchfunction}{touch()} function. Finally, the \c touch command. Finally, the
\c{.depends} member specifies that \c mytarget depends on \c mytarget2, \c{.depends} member specifies that \c mytarget depends on \c mytarget2,
another target that is defined afterwards. \c mytarget2 is a dummy target. another target that is defined afterwards. \c mytarget2 is a dummy target.
It is only defined to echo some text to the console. It is only defined to echo some text to the console.

View File

@ -116,6 +116,7 @@ SOURCES += \
qbytearray.cpp \ qbytearray.cpp \
qbytearraymatcher.cpp \ qbytearraymatcher.cpp \
qcalendar.cpp \ qcalendar.cpp \
qcborvalue.cpp \
qcryptographichash.cpp \ qcryptographichash.cpp \
qdatetime.cpp \ qdatetime.cpp \
qdir.cpp \ qdir.cpp \
@ -131,8 +132,8 @@ SOURCES += \
qgregoriancalendar.cpp \ qgregoriancalendar.cpp \
qhash.cpp \ qhash.cpp \
qiodevice.cpp \ qiodevice.cpp \
qjson.cpp \
qjsonarray.cpp \ qjsonarray.cpp \
qjsoncbor.cpp \
qjsondocument.cpp \ qjsondocument.cpp \
qjsonobject.cpp \ qjsonobject.cpp \
qjsonparser.cpp \ qjsonparser.cpp \
@ -174,6 +175,8 @@ HEADERS += \
qcalendar.h \ qcalendar.h \
qcalendarbackend_p.h \ qcalendarbackend_p.h \
qcalendarmath_p.h \ qcalendarmath_p.h \
qcborvalue.h \
qcborvalue_p.h \
qchar.h \ qchar.h \
qcryptographichash.h \ qcryptographichash.h \
qdatetime.h \ qdatetime.h \

View File

@ -1100,6 +1100,12 @@ Mozilla License) is included. The data is then also used in QNetworkCookieJar::v
Note that this is required for plugin loading. Qt GUI needs QPA plugins for basic operation.", Note that this is required for plugin loading. Qt GUI needs QPA plugins for basic operation.",
"section": "Utilities", "section": "Utilities",
"output": [ "publicFeature" ] "output": [ "publicFeature" ]
},
"binaryjson": {
"label": "Binary JSON (deprecated)",
"purpose": "Provides support for the deprecated binary JSON format.",
"section": "Utilities",
"output": [ "publicFeature" ]
} }
}, },

View File

@ -74,6 +74,7 @@
#else #else
# define QT_FEATURE_alloca_malloc_h -1 # define QT_FEATURE_alloca_malloc_h -1
#endif #endif
#define QT_FEATURE_binaryjson -1
#define QT_FEATURE_cborstream -1 #define QT_FEATURE_cborstream -1
#define QT_CRYPTOGRAPHICHASH_ONLY_SHA1 #define QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#define QT_FEATURE_cxx11_random (QT_HAS_INCLUDE(<random>) ? 1 : -1) #define QT_FEATURE_cxx11_random (QT_HAS_INCLUDE(<random>) ? 1 : -1)

View File

@ -327,9 +327,9 @@ public:
return pre; return pre;
} }
static constexpr QSpecialInteger max() static Q_DECL_CONSTEXPR QSpecialInteger max()
{ return QSpecialInteger(std::numeric_limits<T>::max()); } { return QSpecialInteger(std::numeric_limits<T>::max()); }
static constexpr QSpecialInteger min() static Q_DECL_CONSTEXPR QSpecialInteger min()
{ return QSpecialInteger(std::numeric_limits<T>::min()); } { return QSpecialInteger(std::numeric_limits<T>::min()); }
}; };
@ -373,8 +373,8 @@ public:
QLEInteger &operator ++(int); QLEInteger &operator ++(int);
QLEInteger &operator --(int); QLEInteger &operator --(int);
static constexpr QLEInteger max(); static Q_DECL_CONSTEXPR QLEInteger max();
static constexpr QLEInteger min(); static Q_DECL_CONSTEXPR QLEInteger min();
}; };
template<typename T> template<typename T>
@ -400,8 +400,8 @@ public:
QBEInteger &operator ++(int); QBEInteger &operator ++(int);
QBEInteger &operator --(int); QBEInteger &operator --(int);
static constexpr QBEInteger max(); static Q_DECL_CONSTEXPR QBEInteger max();
static constexpr QBEInteger min(); static Q_DECL_CONSTEXPR QBEInteger min();
}; };
#else #else

View File

@ -114,8 +114,8 @@ extern "C" {
// without full system POSIX. // without full system POSIX.
# pragma weak shm_area_password # pragma weak shm_area_password
# pragma weak shm_area_name # pragma weak shm_area_name
char *shm_area_password = "dummy"; char shm_area_password[] = "dummy";
char *shm_area_name = "dummy"; char shm_area_name[] = "dummy";
} }
#endif #endif

View File

@ -58,6 +58,36 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#define Q_RETURN_ON_INVALID_FILENAME(message, result) \
{ \
QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).warning(message); \
errno = EINVAL; \
return (result); \
}
inline bool qIsFilenameBroken(const QByteArray &name)
{
return name.contains('\0');
}
inline bool qIsFilenameBroken(const QString &name)
{
return name.contains(QLatin1Char('\0'));
}
inline bool qIsFilenameBroken(const QFileSystemEntry &entry)
{
return qIsFilenameBroken(entry.nativeFilePath());
}
#define Q_CHECK_FILE_NAME(name, result) \
do { \
if (Q_UNLIKELY((name).isEmpty())) \
Q_RETURN_ON_INVALID_FILENAME("Empty filename passed to function", (result)); \
if (Q_UNLIKELY(qIsFilenameBroken(name))) \
Q_RETURN_ON_INVALID_FILENAME("Broken filename passed to function", (result)); \
} while (false)
class QFileSystemEngine class QFileSystemEngine
{ {
public: public:

View File

@ -118,13 +118,6 @@ enum {
#endif #endif
}; };
#define emptyFileEntryWarning() emptyFileEntryWarning_(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC)
static void emptyFileEntryWarning_(const char *file, int line, const char *function)
{
QMessageLogger(file, line, function).warning("Empty filename passed to function");
errno = EINVAL;
}
#if defined(Q_OS_DARWIN) #if defined(Q_OS_DARWIN)
static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data, static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
const QFileSystemEntry &entry, const QFileSystemEntry &entry,
@ -625,8 +618,7 @@ void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
//static //static
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data) QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
{ {
if (Q_UNLIKELY(link.isEmpty())) Q_CHECK_FILE_NAME(link, link);
return emptyFileEntryWarning(), link;
QByteArray s = qt_readlink(link.nativeFilePath().constData()); QByteArray s = qt_readlink(link.nativeFilePath().constData());
if (s.length() > 0) { if (s.length() > 0) {
@ -685,10 +677,7 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static //static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, entry);
return emptyFileEntryWarning(), entry;
if (entry.isRoot())
return entry;
#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L #if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
// realpath(X,0) is not supported // realpath(X,0) is not supported
@ -738,8 +727,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static //static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, entry);
return emptyFileEntryWarning(), entry;
if (entry.isAbsolute() && entry.isClean()) if (entry.isAbsolute() && entry.isClean())
return entry; return entry;
@ -773,8 +762,7 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
//static //static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, QByteArray());
return emptyFileEntryWarning(), QByteArray();
QT_STATBUF statResult; QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) { if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
@ -887,8 +875,7 @@ QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry)
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what) QFileSystemMetaData::MetaDataFlags what)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, false);
return emptyFileEntryWarning(), false;
#if defined(Q_OS_DARWIN) #if defined(Q_OS_DARWIN)
if (what & QFileSystemMetaData::BundleType) { if (what & QFileSystemMetaData::BundleType) {
@ -1157,8 +1144,7 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{ {
QString dirName = entry.filePath(); QString dirName = entry.filePath();
if (Q_UNLIKELY(dirName.isEmpty())) Q_CHECK_FILE_NAME(dirName, false);
return emptyFileEntryWarning(), false;
// Darwin doesn't support trailing /'s, so remove for everyone // Darwin doesn't support trailing /'s, so remove for everyone
while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/'))) while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/')))
@ -1177,8 +1163,7 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
//static //static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, false);
return emptyFileEntryWarning(), false;
if (removeEmptyParents) { if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath()); QString dirName = QDir::cleanPath(entry.filePath());
@ -1203,8 +1188,9 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
//static //static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{ {
if (Q_UNLIKELY(source.isEmpty() || target.isEmpty())) Q_CHECK_FILE_NAME(source, false);
return emptyFileEntryWarning(), false; Q_CHECK_FILE_NAME(target, false);
if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true; return true;
error = QSystemError(errno, QSystemError::StandardLibraryError); error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1233,8 +1219,9 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
{ {
QFileSystemEntry::NativePath srcPath = source.nativeFilePath(); QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
QFileSystemEntry::NativePath tgtPath = target.nativeFilePath(); QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty()))
return emptyFileEntryWarning(), false; Q_CHECK_FILE_NAME(srcPath, false);
Q_CHECK_FILE_NAME(tgtPath, false);
#if defined(RENAME_NOREPLACE) && QT_CONFIG(renameat2) #if defined(RENAME_NOREPLACE) && QT_CONFIG(renameat2)
if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0) if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
@ -1302,8 +1289,9 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static //static
bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{ {
if (Q_UNLIKELY(source.isEmpty() || target.isEmpty())) Q_CHECK_FILE_NAME(source, false);
return emptyFileEntryWarning(), false; Q_CHECK_FILE_NAME(target, false);
if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true; return true;
error = QSystemError(errno, QSystemError::StandardLibraryError); error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1313,8 +1301,7 @@ bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, cons
//static //static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, false);
return emptyFileEntryWarning(), false;
if (unlink(entry.nativeFilePath().constData()) == 0) if (unlink(entry.nativeFilePath().constData()) == 0)
return true; return true;
error = QSystemError(errno, QSystemError::StandardLibraryError); error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1349,8 +1336,7 @@ static mode_t toMode_t(QFile::Permissions permissions)
//static //static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data) bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
{ {
if (Q_UNLIKELY(entry.isEmpty())) Q_CHECK_FILE_NAME(entry, false);
return emptyFileEntryWarning(), false;
mode_t mode = toMode_t(permissions); mode_t mode = toMode_t(permissions);
bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0; bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;

View File

@ -461,7 +461,9 @@ void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
QFileSystemMetaData &data) QFileSystemMetaData &data)
{ {
if (data.missingFlags(QFileSystemMetaData::LinkType)) Q_CHECK_FILE_NAME(link, link);
if (data.missingFlags(QFileSystemMetaData::LinkType))
QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType); QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType);
QString target; QString target;
@ -480,6 +482,8 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static //static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{ {
Q_CHECK_FILE_NAME(entry, entry);
if (data.missingFlags(QFileSystemMetaData::ExistsAttribute)) if (data.missingFlags(QFileSystemMetaData::ExistsAttribute))
QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute); QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute);
@ -492,6 +496,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static //static
QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path) QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
{ {
Q_CHECK_FILE_NAME(path, QString());
// can be //server or //server/share // can be //server or //server/share
QString absPath; QString absPath;
QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1)); QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
@ -527,6 +533,8 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
//static //static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{ {
Q_CHECK_FILE_NAME(entry, entry);
QString ret; QString ret;
if (!entry.isRelative()) { if (!entry.isRelative()) {
@ -609,6 +617,8 @@ QByteArray fileIdWin8(HANDLE handle)
//static //static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{ {
Q_CHECK_FILE_NAME(entry, QByteArray());
QByteArray result; QByteArray result;
#ifndef Q_OS_WINRT #ifndef Q_OS_WINRT
@ -999,6 +1009,7 @@ static bool isDirPath(const QString &dirPath, bool *existed);
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what) QFileSystemMetaData::MetaDataFlags what)
{ {
Q_CHECK_FILE_NAME(entry, false);
what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags; what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags;
data.entryFlags &= ~what; data.entryFlags &= ~what;
@ -1116,6 +1127,8 @@ static bool isDirPath(const QString &dirPath, bool *existed)
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{ {
QString dirName = entry.filePath(); QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
if (createParents) { if (createParents) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
// We spefically search for / so \ would break it.. // We spefically search for / so \ would break it..
@ -1177,6 +1190,8 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{ {
QString dirName = entry.filePath(); QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
if (removeEmptyParents) { if (removeEmptyParents) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
@ -1381,6 +1396,9 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
//static //static
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{ {
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
#ifndef Q_OS_WINRT #ifndef Q_OS_WINRT
bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(), bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
(wchar_t*)target.nativeFilePath().utf16()) != 0; (wchar_t*)target.nativeFilePath().utf16()) != 0;
@ -1396,6 +1414,9 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static //static
bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{ {
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()), bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()), reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
MOVEFILE_REPLACE_EXISTING) != 0; MOVEFILE_REPLACE_EXISTING) != 0;
@ -1407,6 +1428,8 @@ bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, cons
//static //static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{ {
Q_CHECK_FILE_NAME(entry, false);
bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0; bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
if(!ret) if(!ret)
error = QSystemError(::GetLastError(), QSystemError::NativeError); error = QSystemError(::GetLastError(), QSystemError::NativeError);
@ -1417,6 +1440,8 @@ bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data) QFileSystemMetaData *data)
{ {
Q_CHECK_FILE_NAME(entry, false);
Q_UNUSED(data); Q_UNUSED(data);
int mode = 0; int mode = 0;

View File

@ -142,36 +142,45 @@ QString QStandardPaths::writableLocation(StandardLocation type)
} }
case RuntimeLocation: case RuntimeLocation:
{ {
const uint myUid = uint(geteuid());
// http://standards.freedesktop.org/basedir-spec/latest/ // http://standards.freedesktop.org/basedir-spec/latest/
const uint myUid = uint(geteuid());
// since the current user is the owner, set both xxxUser and xxxOwner
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
QFileInfo fileInfo; QFileInfo fileInfo;
QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR")); QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
if (xdgRuntimeDir.isEmpty()) { if (xdgRuntimeDir.isEmpty()) {
const QString userName = QFileSystemEngine::resolveUserName(myUid); const QString userName = QFileSystemEngine::resolveUserName(myUid);
xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName; xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName;
fileInfo.setFile(xdgRuntimeDir); fileInfo.setFile(xdgRuntimeDir);
if (!fileInfo.isDir()) {
if (!QDir().mkdir(xdgRuntimeDir)) {
qErrnoWarning("QStandardPaths: error creating runtime directory %ls",
qUtf16Printable(xdgRuntimeDir));
return QString();
}
}
#ifndef Q_OS_WASM #ifndef Q_OS_WASM
qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%ls'", qUtf16Printable(xdgRuntimeDir)); qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%ls'", qUtf16Printable(xdgRuntimeDir));
#endif #endif
} else { } else {
fileInfo.setFile(xdgRuntimeDir); fileInfo.setFile(xdgRuntimeDir);
if (!fileInfo.exists()) { }
qWarning("QStandardPaths: XDG_RUNTIME_DIR points to non-existing path '%ls', " if (fileInfo.exists()) {
"please create it with 0700 permissions.", qUtf16Printable(xdgRuntimeDir));
return QString();
}
if (!fileInfo.isDir()) { if (!fileInfo.isDir()) {
qWarning("QStandardPaths: XDG_RUNTIME_DIR points to '%ls' which is not a directory", qWarning("QStandardPaths: XDG_RUNTIME_DIR points to '%ls' which is not a directory",
qUtf16Printable(xdgRuntimeDir)); qUtf16Printable(xdgRuntimeDir));
return QString(); return QString();
} }
} else {
QFileSystemEntry entry(xdgRuntimeDir);
if (!QFileSystemEngine::createDirectory(entry, false)) {
if (errno != EEXIST) {
qErrnoWarning("QStandardPaths: error creating runtime directory %ls",
qUtf16Printable(xdgRuntimeDir));
return QString();
}
} else {
QSystemError error;
if (!QFileSystemEngine::setPermissions(entry, wantedPerms, error)) {
qWarning("QStandardPaths: could not set correct permissions on runtime directory %ls: %ls",
qUtf16Printable(xdgRuntimeDir), qUtf16Printable(error.toString()));
return QString();
}
}
} }
// "The directory MUST be owned by the user" // "The directory MUST be owned by the user"
if (fileInfo.ownerId() != myUid) { if (fileInfo.ownerId() != myUid) {
@ -181,17 +190,12 @@ QString QStandardPaths::writableLocation(StandardLocation type)
return QString(); return QString();
} }
// "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700." // "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700."
// since the current user is the owner, set both xxxUser and xxxOwner
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
if (fileInfo.permissions() != wantedPerms) { if (fileInfo.permissions() != wantedPerms) {
QFile file(xdgRuntimeDir); qWarning("QStandardPaths: wrong permissions on runtime directory %ls, %x instead of %x",
if (!file.setPermissions(wantedPerms)) { qUtf16Printable(xdgRuntimeDir), uint(fileInfo.permissions()), uint(wantedPerms));
qWarning("QStandardPaths: could not set correct permissions on runtime directory %ls: %ls", return QString();
qUtf16Printable(xdgRuntimeDir), qUtf16Printable(file.errorString()));
return QString();
}
} }
return xdgRuntimeDir; return xdgRuntimeDir;
} }
default: default:

View File

@ -171,11 +171,11 @@ public:
QModelIndex currentIndex() const; QModelIndex currentIndex() const;
Q_INVOKABLE bool isSelected(const QModelIndex &index) const; Q_INVOKABLE bool isSelected(const QModelIndex &index) const;
Q_INVOKABLE bool isRowSelected(int row, const QModelIndex &parent) const; Q_INVOKABLE bool isRowSelected(int row, const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE bool isColumnSelected(int column, const QModelIndex &parent) const; Q_INVOKABLE bool isColumnSelected(int column, const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE bool rowIntersectsSelection(int row, const QModelIndex &parent) const; Q_INVOKABLE bool rowIntersectsSelection(int row, const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE bool columnIntersectsSelection(int column, const QModelIndex &parent) const; Q_INVOKABLE bool columnIntersectsSelection(int column, const QModelIndex &parent = QModelIndex()) const;
bool hasSelection() const; bool hasSelection() const;

View File

@ -0,0 +1,415 @@
/****************************************************************************
**
** 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 "qbinaryjson_p.h"
#include <qjsonobject.h>
#include <qjsonarray.h>
QT_BEGIN_NAMESPACE
namespace QBinaryJsonPrivate {
static Q_CONSTEXPR Base emptyArray = {
{ qle_uint(sizeof(Base)) },
{ 0 },
{ qle_uint(0) }
};
static Q_CONSTEXPR Base emptyObject = {
{ qle_uint(sizeof(Base)) },
{ qToLittleEndian(1U) },
{ qle_uint(0) }
};
void MutableData::compact()
{
Q_STATIC_ASSERT(sizeof(Value) == sizeof(offset));
Base *base = header->root();
int reserve = 0;
if (base->is_object) {
auto *o = static_cast<Object *>(base);
for (uint i = 0; i < o->length; ++i)
reserve += o->entryAt(i)->usedStorage(o);
} else {
auto *a = static_cast<Array *>(base);
for (uint i = 0; i < a->length; ++i)
reserve += a->at(i)->usedStorage(a);
}
uint size = sizeof(Base) + reserve + base->length * sizeof(offset);
uint alloc = sizeof(Header) + size;
auto *h = reinterpret_cast<Header *>(malloc(alloc));
Q_CHECK_PTR(h);
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);
uint offset = sizeof(Base);
if (b->is_object) {
const auto *o = static_cast<const Object *>(base);
auto *no = static_cast<Object *>(b);
for (uint i = 0; i < o->length; ++i) {
no->table()[i] = offset;
const Entry *e = o->entryAt(i);
Entry *ne = no->entryAt(i);
uint s = e->size();
memcpy(ne, e, s);
offset += s;
uint dataSize = e->value.usedStorage(o);
if (dataSize) {
memcpy(reinterpret_cast<char *>(no) + offset, e->value.data(o), dataSize);
ne->value.value = offset;
offset += dataSize;
}
}
} else {
const auto *a = static_cast<const Array *>(base);
auto *na = static_cast<Array *>(b);
for (uint i = 0; i < a->length; ++i) {
const Value *v = a->at(i);
Value *nv = na->at(i);
*nv = *v;
uint dataSize = v->usedStorage(a);
if (dataSize) {
memcpy(reinterpret_cast<char *>(na) + offset, v->data(a), dataSize);
nv->value = offset;
offset += dataSize;
}
}
}
Q_ASSERT(offset == uint(b->tableOffset));
free(header);
header = h;
this->alloc = alloc;
compactionCounter = 0;
}
bool ConstData::isValid() const
{
if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1U)
return false;
const Base *root = header->root();
const uint maxSize = alloc - sizeof(Header);
return root->is_object
? static_cast<const Object *>(root)->isValid(maxSize)
: static_cast<const Array *>(root)->isValid(maxSize);
}
QJsonDocument ConstData::toJsonDocument() const
{
const Base *root = header->root();
return root->is_object
? QJsonDocument(static_cast<const Object *>(root)->toJsonObject())
: QJsonDocument(static_cast<const Array *>(root)->toJsonArray());
}
uint Base::reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace)
{
Q_ASSERT(posInTable <= 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(reinterpret_cast<char *>(table()) + dataSize, table(), length * sizeof(offset));
} else {
memmove(reinterpret_cast<char *>(table() + posInTable + numItems) + dataSize,
table() + posInTable, (length - posInTable) * sizeof(offset));
memmove(reinterpret_cast<char *>(table()) + dataSize, table(), posInTable * sizeof(offset));
}
tableOffset += dataSize;
for (uint i = 0; i < numItems; ++i)
table()[posInTable + i] = off;
size += dataSize;
if (!replace) {
length += numItems;
size += numItems * sizeof(offset);
}
return off;
}
uint Object::indexOf(QStringView key, bool *exists) const
{
uint min = 0;
uint n = length;
while (n > 0) {
uint half = n >> 1;
uint middle = min + half;
if (*entryAt(middle) >= key) {
n = half;
} else {
min = middle + 1;
n -= half + 1;
}
}
if (min < length && *entryAt(min) == key) {
*exists = true;
return min;
}
*exists = false;
return min;
}
QJsonObject Object::toJsonObject() const
{
QJsonObject object;
for (uint i = 0; i < length; ++i) {
const Entry *e = entryAt(i);
object.insert(e->key(), e->value.toJsonValue(this));
}
return object;
}
bool Object::isValid(uint maxSize) const
{
if (size > maxSize || tableOffset + length * sizeof(offset) > size)
return false;
QString lastKey;
for (uint i = 0; i < length; ++i) {
if (table()[i] + sizeof(Entry) >= tableOffset)
return false;
const Entry *e = entryAt(i);
if (!e->isValid(tableOffset - table()[i]))
return false;
const QString key = e->key();
if (key < lastKey)
return false;
if (!e->value.isValid(this))
return false;
lastKey = key;
}
return true;
}
QJsonArray Array::toJsonArray() const
{
QJsonArray array;
const offset *values = table();
for (uint i = 0; i < length; ++i)
array.append(reinterpret_cast<const Value *>(values + i)->toJsonValue(this));
return array;
}
bool Array::isValid(uint maxSize) const
{
if (size > maxSize || tableOffset + length * sizeof(offset) > size)
return false;
const offset *values = table();
for (uint i = 0; i < length; ++i) {
if (!reinterpret_cast<const Value *>(values + i)->isValid(this))
return false;
}
return true;
}
uint Value::usedStorage(const Base *b) const
{
uint s = 0;
switch (type) {
case QJsonValue::Double:
if (!latinOrIntValue)
s = sizeof(double);
break;
case QJsonValue::String: {
const char *d = data(b);
s = latinOrIntValue
? (sizeof(ushort)
+ qFromLittleEndian(*reinterpret_cast<const ushort *>(d)))
: (sizeof(int)
+ sizeof(ushort) * qFromLittleEndian(*reinterpret_cast<const 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);
}
QJsonValue Value::toJsonValue(const Base *b) const
{
switch (type) {
case QJsonValue::Null:
return QJsonValue(QJsonValue::Null);
case QJsonValue::Bool:
return QJsonValue(toBoolean());
case QJsonValue::Double:
return QJsonValue(toDouble(b));
case QJsonValue::String:
return QJsonValue(toString(b));
case QJsonValue::Array:
return static_cast<const Array *>(base(b))->toJsonArray();
case QJsonValue::Object:
return static_cast<const Object *>(base(b))->toJsonObject();
case QJsonValue::Undefined:
return QJsonValue(QJsonValue::Undefined);
}
Q_UNREACHABLE();
return QJsonValue(QJsonValue::Undefined);
}
inline bool isValidValueOffset(uint offset, uint tableOffset)
{
return offset >= sizeof(Base)
&& offset + sizeof(uint) <= tableOffset;
}
bool Value::isValid(const Base *b) const
{
switch (type) {
case QJsonValue::Null:
case QJsonValue::Bool:
return true;
case QJsonValue::Double:
return latinOrIntValue || isValidValueOffset(value, b->tableOffset);
case QJsonValue::String:
if (!isValidValueOffset(value, b->tableOffset))
return false;
if (latinOrIntValue)
return asLatin1String(b).isValid(b->tableOffset - value);
return asString(b).isValid(b->tableOffset - value);
case QJsonValue::Array:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<const Array *>(base(b))->isValid(b->tableOffset - value);
case QJsonValue::Object:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<const Object *>(base(b))->isValid(b->tableOffset - value);
default:
return false;
}
}
uint Value::requiredStorage(const QBinaryJsonValue &v, bool *compressed)
{
*compressed = false;
switch (v.type()) {
case QJsonValue::Double:
if (QBinaryJsonPrivate::compressedNumber(v.toDouble()) != INT_MAX) {
*compressed = true;
return 0;
}
return sizeof(double);
case QJsonValue::String: {
QString s = v.toString();
*compressed = QBinaryJsonPrivate::useCompressed(s);
return QBinaryJsonPrivate::qStringSize(s, *compressed);
}
case QJsonValue::Array:
case QJsonValue::Object:
return v.base ? uint(v.base->size) : sizeof(QBinaryJsonPrivate::Base);
case QJsonValue::Undefined:
case QJsonValue::Null:
case QJsonValue::Bool:
break;
}
return 0;
}
uint Value::valueToStore(const QBinaryJsonValue &v, uint offset)
{
switch (v.type()) {
case QJsonValue::Undefined:
case QJsonValue::Null:
break;
case QJsonValue::Bool:
return v.toBool();
case QJsonValue::Double: {
int c = QBinaryJsonPrivate::compressedNumber(v.toDouble());
if (c != INT_MAX)
return c;
}
Q_FALLTHROUGH();
case QJsonValue::String:
case QJsonValue::Array:
case QJsonValue::Object:
return offset;
}
return 0;
}
void Value::copyData(const QBinaryJsonValue &v, char *dest, bool compressed)
{
switch (v.type()) {
case QJsonValue::Double:
if (!compressed)
qToLittleEndian(v.toDouble(), dest);
break;
case QJsonValue::String: {
const QString str = v.toString();
QBinaryJsonPrivate::copyString(dest, str, compressed);
break;
}
case QJsonValue::Array:
case QJsonValue::Object: {
const QBinaryJsonPrivate::Base *b = v.base;
if (!b)
b = (v.type() == QJsonValue::Array ? &emptyArray : &emptyObject);
memcpy(dest, b, b->size);
break;
}
default:
break;
}
}
} // namespace QBinaryJsonPrivate
QT_END_NAMESPACE

View File

@ -0,0 +1,617 @@
/****************************************************************************
**
** 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 QBINARYJSON_P_H
#define QBINARYJSON_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 <private/qbinaryjsonvalue_p.h>
#include <private/qendian_p.h>
#include <qjsondocument.h>
#include <limits>
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
// in qstring.cpp
void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len);
void qt_from_latin1(ushort *dst, const char *str, size_t size) noexcept;
/*
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.
*/
namespace QBinaryJsonPrivate {
class Array;
class Object;
class Value;
class Entry;
template<typename T>
using q_littleendian = QLEInteger<T>;
using qle_short = q_littleendian<short>;
using qle_ushort = q_littleendian<unsigned short>;
using qle_int = q_littleendian<int>;
using qle_uint = q_littleendian<unsigned int>;
template<int pos, int width>
using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
template<int pos, int width>
using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
using offset = qle_uint;
// round the size up to the next 4 byte boundary
inline uint alignedSize(uint size) { return (size + 3) & ~3; }
const int MaxLatin1Length = 0x7fff;
static inline bool useCompressed(QStringView s)
{
if (s.length() > MaxLatin1Length)
return false;
return QtPrivate::isLatin1(s);
}
static inline bool useCompressed(QLatin1String s)
{
return s.size() <= MaxLatin1Length;
}
static inline uint qStringSize(const QString &string, bool compress)
{
uint l = 2 + string.size();
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 std::numeric_limits<int>::max();
quint64 non_int = val & (fraction_mask >> exp);
if (non_int)
return std::numeric_limits<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(reinterpret_cast<const Data *>(data)) {}
struct Data {
qle_uint length;
qle_ushort utf16[1];
};
const Data *d;
uint byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; }
bool isValid(uint maxSize) const
{
// Check byteSize() <= maxSize, avoiding integer overflow
return maxSize >= sizeof(uint)
&& uint(d->length) <= (maxSize - sizeof(uint)) / sizeof(ushort);
}
static void copy(char *dest, QStringView str)
{
Data *data = reinterpret_cast<Data *>(dest);
data->length = str.length();
qToLittleEndian<quint16>(str.utf16(), str.length(), data->utf16);
fillTrailingZeros(data);
}
static void fillTrailingZeros(Data *data)
{
if (data->length & 1)
data->utf16[data->length] = 0;
}
bool operator ==(QStringView str) const
{
int slen = str.length();
int l = d->length;
if (slen != l)
return false;
const auto *s = reinterpret_cast<const ushort *>(str.utf16());
const qle_ushort *a = d->utf16;
const ushort *b = s;
while (l-- && *a == *b)
a++,b++;
return (l == -1);
}
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));
}
QString toString() const
{
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
return QString(reinterpret_cast<const QChar *>(d->utf16), d->length);
#else
const uint l = d->length;
QString str(l, Qt::Uninitialized);
QChar *ch = str.data();
for (uint i = 0; i < l; ++i)
ch[i] = QChar(d->utf16[i]);
return str;
#endif
}
};
class Latin1String
{
public:
explicit Latin1String(const char *data) : d(reinterpret_cast<const Data *>(data)) {}
struct Data {
qle_ushort length;
char latin1[1];
};
const Data *d;
uint byteSize() const { return sizeof(ushort) + sizeof(char) * (d->length); }
bool isValid(uint maxSize) const { return byteSize() <= maxSize; }
static void copy(char *dest, QStringView src)
{
Data *data = reinterpret_cast<Data *>(dest);
data->length = src.length();
auto *l = reinterpret_cast<uchar *>(data->latin1);
const auto *uc = reinterpret_cast<const ushort *>(src.utf16());
qt_to_latin1_unchecked(l, uc, data->length);
for (uint len = data->length; quintptr(l + len) & 0x3; ++len)
l[len] = 0;
}
QLatin1String toQLatin1String() const noexcept { return QLatin1String(d->latin1, d->length); }
QString toString() const { return QString::fromLatin1(d->latin1, d->length); }
};
static inline void copyString(char *dest, QStringView str, bool compress)
{
if (compress)
Latin1String::copy(dest, str);
else
String::copy(dest, str);
}
/*
Base is the base class for both Object and Array. Both classes 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
bool isObject() const { return !!is_object; }
bool isArray() const { return !isObject(); }
offset *table()
{
return reinterpret_cast<offset *>(reinterpret_cast<char *>(this) + tableOffset);
}
const offset *table() const
{
return reinterpret_cast<const offset *>(reinterpret_cast<const char *>(this) + tableOffset);
}
uint reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace);
};
class Object : public Base
{
public:
const Entry *entryAt(uint i) const
{
return reinterpret_cast<const Entry *>(reinterpret_cast<const char *>(this) + table()[i]);
}
Entry *entryAt(uint i)
{
return reinterpret_cast<Entry *>(reinterpret_cast<char *>(this) + table()[i]);
}
uint indexOf(QStringView key, bool *exists) const;
QJsonObject toJsonObject() const;
bool isValid(uint maxSize) const;
};
class Array : public Base
{
public:
const Value *at(uint i) const { return reinterpret_cast<const Value *>(table() + i); }
Value *at(uint i) { return reinterpret_cast<Value *>(table() + i); }
QJsonArray toJsonArray() const;
bool isValid(uint 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 const char *data(const Base *b) const
{
return reinterpret_cast<const char *>(b) + value;
}
uint usedStorage(const Base *b) const;
bool toBoolean() const
{
Q_ASSERT(type == QJsonValue::Bool);
return value != 0;
}
double toDouble(const Base *b) const
{
Q_ASSERT(type == QJsonValue::Double);
if (latinOrIntValue)
return int_value;
auto i = qFromLittleEndian<quint64>(reinterpret_cast<const uchar *>(b) + value);
double d;
memcpy(&d, &i, sizeof(double));
return d;
}
QString toString(const Base *b) const
{
return latinOrIntValue
? asLatin1String(b).toString()
: asString(b).toString();
}
String asString(const Base *b) const
{
Q_ASSERT(type == QJsonValue::String && !latinOrIntValue);
return String(data(b));
}
Latin1String asLatin1String(const Base *b) const
{
Q_ASSERT(type == QJsonValue::String && latinOrIntValue);
return Latin1String(data(b));
}
const Base *base(const Base *b) const
{
Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object);
return reinterpret_cast<const Base *>(data(b));
}
QJsonValue toJsonValue(const Base *b) const;
bool isValid(const Base *b) const;
static uint requiredStorage(const QBinaryJsonValue &v, bool *compressed);
static uint valueToStore(const QBinaryJsonValue &v, uint offset);
static void copyData(const QBinaryJsonValue &v, char *dest, bool compressed);
};
class Entry {
public:
Value value;
// key
// value data follows key
uint size() const
{
uint s = sizeof(Entry);
if (value.latinKey)
s += shallowLatin1Key().byteSize();
else
s += shallowKey().byteSize();
return alignedSize(s);
}
uint usedStorage(Base *b) const
{
return size() + value.usedStorage(b);
}
String shallowKey() const
{
Q_ASSERT(!value.latinKey);
return String(reinterpret_cast<const char *>(this) + sizeof(Entry));
}
Latin1String shallowLatin1Key() const
{
Q_ASSERT(value.latinKey);
return Latin1String(reinterpret_cast<const char *>(this) + sizeof(Entry));
}
QString key() const
{
return value.latinKey
? shallowLatin1Key().toString()
: shallowKey().toString();
}
bool isValid(uint maxSize) const
{
if (maxSize < sizeof(Entry))
return false;
maxSize -= sizeof(Entry);
return value.latinKey
? shallowLatin1Key().isValid(maxSize)
: shallowKey().isValid(maxSize);
}
bool operator ==(QStringView key) const
{
return value.latinKey
? (shallowLatin1Key().toQLatin1String() == key)
: (shallowKey() == key);
}
bool operator >=(QStringView key) const
{
return value.latinKey
? (shallowLatin1Key().toQLatin1String() >= key)
: (shallowKey().toString() >= key);
}
};
class Header {
public:
qle_uint tag; // 'qbjs'
qle_uint version; // 1
Base *root() { return reinterpret_cast<Base *>(this + 1); }
const Base *root() const { return reinterpret_cast<const Base *>(this + 1); }
};
class ConstData
{
Q_DISABLE_COPY_MOVE(ConstData)
public:
const uint alloc;
union {
const char *rawData;
const Header *header;
};
ConstData(const char *raw, uint a) : alloc(a), rawData(raw) {}
bool isValid() const;
QJsonDocument toJsonDocument() const;
};
class MutableData
{
Q_DISABLE_COPY_MOVE(MutableData)
public:
QAtomicInt ref;
uint alloc;
union {
char *rawData;
Header *header;
};
uint compactionCounter : 31;
MutableData(char *raw, uint a)
: alloc(a), rawData(raw), compactionCounter(0)
{
}
MutableData(uint reserved, QJsonValue::Type valueType)
: rawData(nullptr), compactionCounter(0)
{
Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object);
alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset);
header = reinterpret_cast<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;
}
~MutableData()
{
free(rawData);
}
MutableData *clone(const Base *b, uint reserve = 0)
{
uint size = sizeof(Header) + b->size;
if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve)
return this;
if (reserve) {
if (reserve < 128)
reserve = 128;
size = qMax(size + reserve, qMin(size *2, uint(Value::MaxSize)));
if (size > Value::MaxSize) {
qWarning("QJson: Document too large to store in data structure");
return nullptr;
}
}
char *raw = reinterpret_cast<char *>(malloc(size));
Q_CHECK_PTR(raw);
memcpy(raw + sizeof(Header), b, b->size);
auto *h = reinterpret_cast<Header *>(raw);
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1;
auto *d = new MutableData(raw, size);
d->compactionCounter = (b == header->root()) ? compactionCounter : 0;
return d;
}
char *takeRawData(uint *size)
{
*size = alloc;
char *result = rawData;
rawData = nullptr;
alloc = 0;
return result;
}
void compact();
};
} // namespace QBinaryJsonPrivate
Q_DECLARE_TYPEINFO(QBinaryJsonPrivate::Value, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
#endif // QBINARYJSON_P_H

View File

@ -0,0 +1,137 @@
/****************************************************************************
**
** 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 "qbinaryjsonarray_p.h"
#include "qbinaryjson_p.h"
#include <qjsonarray.h>
QT_BEGIN_NAMESPACE
QBinaryJsonArray::~QBinaryJsonArray()
{
if (d && !d->ref.deref())
delete d;
}
QBinaryJsonArray QBinaryJsonArray::fromJsonArray(const QJsonArray &array)
{
QBinaryJsonArray binary;
for (const QJsonValue &value : array)
binary.append(QBinaryJsonValue::fromJsonValue(value));
if (binary.d) // We want to compact it as it is a root item now
binary.d->compactionCounter++;
binary.compact();
return binary;
}
void QBinaryJsonArray::append(const QBinaryJsonValue &value)
{
const uint i = a ? a->length : 0;
bool compressed;
uint valueSize = QBinaryJsonPrivate::Value::requiredStorage(value, &compressed);
if (!detach(valueSize + sizeof(QBinaryJsonPrivate::Value)))
return;
if (!a->length)
a->tableOffset = sizeof(QBinaryJsonPrivate::Array);
uint valueOffset = a->reserveSpace(valueSize, i, 1, false);
if (!valueOffset)
return;
QBinaryJsonPrivate::Value *v = a->at(i);
v->type = (value.t == QJsonValue::Undefined ? QJsonValue::Null : value.t);
v->latinOrIntValue = compressed;
v->latinKey = false;
v->value = QBinaryJsonPrivate::Value::valueToStore(value, valueOffset);
if (valueSize) {
QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast<char *>(a) + valueOffset,
compressed);
}
}
char *QBinaryJsonArray::takeRawData(uint *size)
{
if (d)
return d->takeRawData(size);
*size = 0;
return nullptr;
}
bool QBinaryJsonArray::detach(uint reserve)
{
if (!d) {
if (reserve >= QBinaryJsonPrivate::Value::MaxSize) {
qWarning("QBinaryJson: Document too large to store in data structure");
return false;
}
d = new QBinaryJsonPrivate::MutableData(reserve, QJsonValue::Array);
a = static_cast<QBinaryJsonPrivate::Array *>(d->header->root());
d->ref.ref();
return true;
}
if (reserve == 0 && d->ref.loadRelaxed() == 1)
return true;
QBinaryJsonPrivate::MutableData *x = d->clone(a, reserve);
if (!x)
return false;
x->ref.ref();
if (!d->ref.deref())
delete d;
d = x;
a = static_cast<QBinaryJsonPrivate::Array *>(d->header->root());
return true;
}
void QBinaryJsonArray::compact()
{
if (!d || !d->compactionCounter)
return;
detach();
d->compact();
a = static_cast<QBinaryJsonPrivate::Array *>(d->header->root());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) 2019 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 QBINARYJSONARRAY_P_H
#define QBINARYJSONARRAY_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 "qbinaryjsonvalue_p.h"
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
class QBinaryJsonArray
{
Q_DISABLE_COPY(QBinaryJsonArray)
public:
QBinaryJsonArray() = default;
~QBinaryJsonArray();
QBinaryJsonArray(QBinaryJsonArray &&other) noexcept
: d(other.d),
a(other.a)
{
other.d = nullptr;
other.a = nullptr;
}
QBinaryJsonArray &operator =(QBinaryJsonArray &&other) noexcept
{
qSwap(d, other.d);
qSwap(a, other.a);
return *this;
}
static QBinaryJsonArray fromJsonArray(const QJsonArray &array);
char *takeRawData(uint *size);
private:
friend class QBinaryJsonValue;
void append(const QBinaryJsonValue &value);
void compact();
bool detach(uint reserve = 0);
QBinaryJsonPrivate::MutableData *d = nullptr;
QBinaryJsonPrivate::Array *a = nullptr;
};
QT_END_NAMESPACE
#endif // QBINARYJSONARRAY_P_H

View File

@ -0,0 +1,149 @@
/****************************************************************************
**
** 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 "qbinaryjsonobject_p.h"
#include "qbinaryjson_p.h"
#include <qjsonobject.h>
QT_BEGIN_NAMESPACE
QBinaryJsonObject::~QBinaryJsonObject()
{
if (d && !d->ref.deref())
delete d;
}
QBinaryJsonObject QBinaryJsonObject::fromJsonObject(const QJsonObject &object)
{
QBinaryJsonObject binary;
for (auto it = object.begin(), end = object.end(); it != end; ++it)
binary.insert(it.key(), QBinaryJsonValue::fromJsonValue(it.value()));
if (binary.d) // We want to compact it as it is a root item now
binary.d->compactionCounter++;
binary.compact();
return binary;
}
void QBinaryJsonObject::insert(const QString &key, const QBinaryJsonValue &value)
{
bool latinOrIntValue;
uint valueSize = QBinaryJsonPrivate::Value::requiredStorage(value, &latinOrIntValue);
bool latinKey = QBinaryJsonPrivate::useCompressed(key);
uint valueOffset = sizeof(QBinaryJsonPrivate::Entry)
+ QBinaryJsonPrivate::qStringSize(key, latinKey);
uint requiredSize = valueOffset + valueSize;
if (!detach(requiredSize + sizeof(QBinaryJsonPrivate::offset))) // offset for the new index entry
return;
if (!o->length)
o->tableOffset = sizeof(QBinaryJsonPrivate::Object);
bool keyExists = false;
uint pos = o->indexOf(key, &keyExists);
if (keyExists)
++d->compactionCounter;
uint off = o->reserveSpace(requiredSize, pos, 1, keyExists);
if (!off)
return;
QBinaryJsonPrivate::Entry *e = o->entryAt(pos);
e->value.type = value.t;
e->value.latinKey = latinKey;
e->value.latinOrIntValue = latinOrIntValue;
e->value.value = QBinaryJsonPrivate::Value::valueToStore(
value, reinterpret_cast<char *>(e) - reinterpret_cast<char *>(o) + valueOffset);
QBinaryJsonPrivate::copyString(reinterpret_cast<char *>(e + 1), key, latinKey);
if (valueSize) {
QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast<char *>(e) + valueOffset,
latinOrIntValue);
}
if (d->compactionCounter > 32U && d->compactionCounter >= unsigned(o->length) / 2U)
compact();
}
char *QBinaryJsonObject::takeRawData(uint *size) const
{
if (d)
return d->takeRawData(size);
*size = 0;
return nullptr;
}
bool QBinaryJsonObject::detach(uint reserve)
{
if (!d) {
if (reserve >= QBinaryJsonPrivate::Value::MaxSize) {
qWarning("QBinaryJson: Document too large to store in data structure");
return false;
}
d = new QBinaryJsonPrivate::MutableData(reserve, QJsonValue::Object);
o = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
d->ref.ref();
return true;
}
if (reserve == 0 && d->ref.loadRelaxed() == 1)
return true;
QBinaryJsonPrivate::MutableData *x = d->clone(o, reserve);
if (!x)
return false;
x->ref.ref();
if (!d->ref.deref())
delete d;
d = x;
o = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
return true;
}
void QBinaryJsonObject::compact()
{
if (!d || !d->compactionCounter)
return;
detach();
d->compact();
o = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2019 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 QBINARYJSONOBJECT_H
#define QBINARYJSONOBJECT_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 "qbinaryjsonvalue_p.h"
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
class QBinaryJsonObject
{
Q_DISABLE_COPY(QBinaryJsonObject)
public:
QBinaryJsonObject() = default;
~QBinaryJsonObject();
QBinaryJsonObject(QBinaryJsonObject &&other) noexcept
: d(other.d), o(other.o)
{
other.d = nullptr;
other.o = nullptr;
}
QBinaryJsonObject &operator =(QBinaryJsonObject &&other) noexcept
{
qSwap(d, other.d);
qSwap(o, other.o);
return *this;
}
static QBinaryJsonObject fromJsonObject(const QJsonObject &object);
char *takeRawData(uint *size) const;
private:
friend class QBinaryJsonValue;
void insert(const QString &key, const QBinaryJsonValue &value);
bool detach(uint reserve = 0);
void compact();
QBinaryJsonPrivate::MutableData *d = nullptr;
QBinaryJsonPrivate::Object *o = nullptr;
};
QT_END_NAMESPACE
#endif // QBINARYJSONOBJECT_P_H

View File

@ -0,0 +1,155 @@
/****************************************************************************
**
** 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 "qbinaryjsonobject_p.h"
#include "qbinaryjsonvalue_p.h"
#include "qbinaryjsonarray_p.h"
#include "qbinaryjson_p.h"
#include <qjsonarray.h>
#include <qjsonobject.h>
QT_BEGIN_NAMESPACE
QBinaryJsonValue::QBinaryJsonValue(QBinaryJsonPrivate::MutableData *data,
QBinaryJsonPrivate::Base *parent,
const QBinaryJsonPrivate::Value &v)
: t(QJsonValue::Type(uint(v.type)))
{
switch (t) {
case QJsonValue::Undefined:
case QJsonValue::Null:
dbl = 0;
break;
case QJsonValue::Bool:
b = v.toBoolean();
break;
case QJsonValue::Double:
dbl = v.toDouble(parent);
break;
case QJsonValue::String: {
QString s = v.toString(parent);
stringData = s.data_ptr();
stringData->ref.ref();
break;
}
case QJsonValue::Array:
case QJsonValue::Object:
d = data;
base = v.base(parent);
break;
}
if (d)
d->ref.ref();
}
QBinaryJsonValue::QBinaryJsonValue(QString string)
: stringData(*reinterpret_cast<QStringData **>(&string)), t(QJsonValue::String)
{
stringData->ref.ref();
}
QBinaryJsonValue::QBinaryJsonValue(const QBinaryJsonArray &a)
: base(a.a), d(a.d), t(QJsonValue::Array)
{
if (d)
d->ref.ref();
}
QBinaryJsonValue::QBinaryJsonValue(const QBinaryJsonObject &o)
: base(o.o), d(o.d), t(QJsonValue::Object)
{
if (d)
d->ref.ref();
}
QBinaryJsonValue::~QBinaryJsonValue()
{
if (t == QJsonValue::String && stringData && !stringData->ref.deref())
free(stringData);
if (d && !d->ref.deref())
delete d;
}
QBinaryJsonValue QBinaryJsonValue::fromJsonValue(const QJsonValue &json)
{
switch (json.type()) {
case QJsonValue::Bool:
return QBinaryJsonValue(json.toBool());
case QJsonValue::Double:
return QBinaryJsonValue(json.toDouble());
case QJsonValue::String:
return QBinaryJsonValue(json.toString());
case QJsonValue::Array:
return QBinaryJsonArray::fromJsonArray(json.toArray());
case QJsonValue::Object:
return QBinaryJsonObject::fromJsonObject(json.toObject());
case QJsonValue::Null:
return QBinaryJsonValue(QJsonValue::Null);
case QJsonValue::Undefined:
return QBinaryJsonValue(QJsonValue::Undefined);
}
Q_UNREACHABLE();
return QBinaryJsonValue(QJsonValue::Null);
}
QString QBinaryJsonValue::toString() const
{
if (t != QJsonValue::String)
return QString();
stringData->ref.ref(); // the constructor below doesn't add a ref.
QStringDataPtr holder = { stringData };
return QString(holder);
}
void QBinaryJsonValue::detach()
{
if (!d)
return;
QBinaryJsonPrivate::MutableData *x = d->clone(base);
x->ref.ref();
if (!d->ref.deref())
delete d;
d = x;
base = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,134 @@
/****************************************************************************
**
** Copyright (C) 2019 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 QBINARYJSONVALUE_P_H
#define QBINARYJSONVALUE_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/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qjsonvalue.h>
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
class QBinaryJsonArray;
class QBinaryJsonObject;
namespace QBinaryJsonPrivate {
class ConstData;
class MutableData;
class Base;
class Value;
class Object;
class Array;
}
class Q_CORE_EXPORT QBinaryJsonValue
{
Q_DISABLE_COPY(QBinaryJsonValue)
public:
explicit QBinaryJsonValue(QJsonValue::Type type) : ui(0), t(type) {}
explicit QBinaryJsonValue(bool b) : b(b), t(QJsonValue::Bool) {}
explicit QBinaryJsonValue(double n) : dbl(n), t(QJsonValue::Double) {}
explicit QBinaryJsonValue(QString s);
QBinaryJsonValue(const QBinaryJsonArray &a);
QBinaryJsonValue(const QBinaryJsonObject &o);
~QBinaryJsonValue();
QBinaryJsonValue(QBinaryJsonValue &&other) noexcept
: ui(other.ui),
d(other.d),
t(other.t)
{
other.ui = 0;
other.d = nullptr;
other.t = QJsonValue::Null;
}
QBinaryJsonValue &operator =(QBinaryJsonValue &&other) noexcept
{
qSwap(ui, other.ui);
qSwap(d, other.d);
qSwap(t, other.t);
return *this;
}
static QBinaryJsonValue fromJsonValue(const QJsonValue &json);
QJsonValue::Type type() const { return t; }
bool toBool() const { return (t == QJsonValue::Bool) && b; }
double toDouble() const { return (t == QJsonValue::Double) ? dbl : 0; }
QString toString() const;
private:
friend class QBinaryJsonPrivate::Value;
friend class QBinaryJsonArray;
friend class QBinaryJsonObject;
QBinaryJsonValue(QBinaryJsonPrivate::MutableData *d, QBinaryJsonPrivate::Base *parent,
const QBinaryJsonPrivate::Value &v);
void detach();
union {
quint64 ui;
bool b;
double dbl;
QStringData *stringData;
const QBinaryJsonPrivate::Base *base;
};
QBinaryJsonPrivate::MutableData *d = nullptr; // needed for Objects and Arrays
QJsonValue::Type t = QJsonValue::Null;
};
QT_END_NAMESPACE
#endif // QBINARYJSONVALUE_P_H

View File

@ -3109,4 +3109,6 @@ QT_END_NAMESPACE
#include "qcborarray.cpp" #include "qcborarray.cpp"
#include "qcbormap.cpp" #include "qcbormap.cpp"
#ifndef QT_NO_QOBJECT
#include "moc_qcborvalue.cpp" #include "moc_qcborvalue.cpp"
#endif

View File

@ -71,6 +71,8 @@ class QCborStreamReader;
class QCborStreamWriter; class QCborStreamWriter;
class QDataStream; class QDataStream;
namespace QJsonPrivate { class Value; }
struct QCborParserError struct QCborParserError
{ {
qint64 offset = 0; qint64 offset = 0;
@ -301,6 +303,8 @@ public:
private: private:
friend class QCborValueRef; friend class QCborValueRef;
friend class QCborContainerPrivate; friend class QCborContainerPrivate;
friend class QJsonPrivate::Value;
qint64 n = 0; qint64 n = 0;
QCborContainerPrivate *container = nullptr; QCborContainerPrivate *container = nullptr;
Type t = Undefined; Type t = Undefined;

View File

@ -1,451 +0,0 @@
/****************************************************************************
**
** 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)) }, { qToLittleEndian(1u) }, { 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);
Q_CHECK_PTR(h);
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(QStringView 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 ==(QStringView 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() == QString(key); // ### conversion to QString
}
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);
}
inline bool isValidValueOffset(uint offset, uint tableOffset)
{
return offset >= sizeof(Base)
&& offset + sizeof(uint) <= tableOffset;
}
bool Value::isValid(const Base *b) const
{
switch (type) {
case QJsonValue::Null:
case QJsonValue::Bool:
return true;
case QJsonValue::Double:
return latinOrIntValue || isValidValueOffset(value, b->tableOffset);
case QJsonValue::String:
if (!isValidValueOffset(value, b->tableOffset))
return false;
if (latinOrIntValue)
return asLatin1String(b).isValid(b->tableOffset - value);
return asString(b).isValid(b->tableOffset - value);
case QJsonValue::Array:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<Array *>(base(b))->isValid(b->tableOffset - value);
case QJsonValue::Object:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<Object *>(base(b))->isValid(b->tableOffset - value);
default:
return false;
}
}
/*!
\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

View File

@ -52,743 +52,170 @@
// We mean it. // We mean it.
// //
#include <qjsonobject.h>
#include <qjsonvalue.h> #include <qjsonvalue.h>
#include <qjsondocument.h> #include <qcborvalue.h>
#include <qjsonarray.h> #include <private/qcborvalue_p.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>
#include <type_traits>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// in qstring.cpp
void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len);
void qt_from_latin1(ushort *dst, const char *str, size_t size) noexcept;
/*
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 { namespace QJsonPrivate {
class Array; template<typename Element, typename ElementsIterator>
class Object; struct ObjectIterator
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; }
const int MaxLatin1Length = 0x7fff;
static inline bool useCompressed(QStringView s)
{ {
if (s.length() > MaxLatin1Length) using pointer = Element *;
return false;
return QtPrivate::isLatin1(s);
}
static inline bool useCompressed(QLatin1String s) struct value_type;
{ struct reference {
return s.size() <= MaxLatin1Length; reference(Element &ref) : m_key(&ref) {}
}
template <typename T> reference() = delete;
static inline int qStringSize(T string, bool compress) ~reference() = default;
{
int l = 2 + string.size();
if (!compress)
l *= 2;
return alignedSize(l);
}
// returns INT_MAX if it can't compress it into 28 bits reference(const reference &other) = default;
static inline int compressedNumber(double d) reference(reference &&other) = default;
{
// 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; reference &operator=(const value_type &value);
memcpy (&val, &d, sizeof(double)); reference &operator=(const reference &other)
int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; {
if (exp < 0 || exp > 25) if (m_key != other.m_key) {
return INT_MAX; key() = other.key();
value() = other.value();
quint64 non_int = val & (fraction_mask >> exp); }
if (non_int) return *this;
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=(QStringView str)
{
d->length = str.length();
qToLittleEndian<quint16>(str.utf16(), str.length(), d->utf16);
fillTrailingZeros();
return *this;
}
inline String &operator=(QLatin1String str)
{
d->length = str.size();
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
for (int i = 0; i < str.size(); ++i)
d->utf16[i] = str[i].unicode();
#else
qt_from_latin1((ushort *)d->utf16, str.data(), str.size());
#endif
fillTrailingZeros();
return *this;
}
void fillTrailingZeros()
{
if (d->length & 1)
d->utf16[d->length] = 0;
}
inline bool operator ==(QStringView str) const {
int slen = str.length();
int l = d->length;
if (slen != l)
return false;
const ushort *s = (const ushort *)str.utf16();
const qle_ushort *a = d->utf16;
const ushort *b = s;
while (l-- && *a == *b)
a++,b++;
return (l == -1);
}
inline bool operator !=(QStringView str) const {
return !operator ==(str);
}
inline bool operator >=(QStringView 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=(QStringView str)
{
int len = d->length = str.length();
uchar *l = (uchar *)d->latin1;
const ushort *uc = (const ushort *)str.utf16();
qt_to_latin1_unchecked(l, uc, len);
fillTrailingZeros();
return *this;
}
inline Latin1String &operator=(QLatin1String str)
{
int len = d->length = str.size();
uchar *l = (uchar *)d->latin1;
memcpy(l, str.data(), len);
fillTrailingZeros();
return *this;
}
void fillTrailingZeros()
{
uchar *l = (uchar *)d->latin1;
for (int len = d->length; (quintptr)(l + len) & 0x3; ++len)
l[len] = 0;
}
QLatin1String toQLatin1String() const noexcept {
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);
} reference &operator=(reference &&other)
inline bool operator ==(const String &str) const { {
return (str == *this); key() = other.key();
} value() = other.value();
inline bool operator >=(const String &str) const { return *this;
return !(*this < str); }
}
inline QString toString() const { Element &key() { return *m_key; }
return QString::fromLatin1(d->latin1, d->length); Element &value() { return *(m_key + 1); }
}
};
#define DEF_OP(op) \ const Element &key() const { return *m_key; }
inline bool operator op(Latin1String lhs, Latin1String rhs) noexcept \ const Element &value() const { return *(m_key + 1); }
{ \
return lhs.toQLatin1String() op rhs.toQLatin1String(); \
} \
inline bool operator op(QLatin1String lhs, Latin1String rhs) noexcept \
{ \
return lhs op rhs.toQLatin1String(); \
} \
inline bool operator op(Latin1String lhs, QLatin1String rhs) noexcept \
{ \
return lhs.toQLatin1String() op rhs; \
} \
inline bool operator op(QStringView lhs, Latin1String rhs) noexcept \
{ \
return lhs op rhs.toQLatin1String(); \
} \
inline bool operator op(Latin1String lhs, QStringView rhs) noexcept \
{ \
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);
}
template <typename T>
static inline void copyString(char *dest, T str, bool compress)
{
if (compress) {
Latin1String string(dest);
string = str;
} else {
String string(dest);
string = str;
}
}
/* private:
Base is the base class for both Object and Array. Both classes work more or less the same way. Element *m_key;
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; } struct value_type {
inline bool isArray() const { return !isObject(); } value_type(reference ref) : m_key(ref.key()), m_value(ref.value()) {}
inline offset *table() const { return (offset *) (((char *) this) + tableOffset); } Element key() const { return m_key; }
Element value() const { return m_value; }
private:
Element m_key;
Element m_value;
};
int reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace); using difference_type = typename QVector<Element>::difference_type;
void removeItems(int pos, int numItems); using iterator_category = std::random_access_iterator_tag;
ObjectIterator() = default;
ObjectIterator(ElementsIterator it) : it(it) {}
ElementsIterator elementsIterator() { return it; }
ObjectIterator operator++(int) { ObjectIterator ret(it); it += 2; return ret; }
ObjectIterator &operator++() { it += 2; return *this; }
ObjectIterator &operator+=(difference_type n) { it += 2 * n; return *this; }
ObjectIterator operator--(int) { ObjectIterator ret(it); it -= 2; return ret; }
ObjectIterator &operator--() { it -= 2; return *this; }
ObjectIterator &operator-=(difference_type n) { it -= 2 * n; return *this; }
reference operator*() const { return *it; }
reference operator[](int n) const { return it[n * 2]; }
bool operator<(ObjectIterator other) const { return it < other.it; }
bool operator>(ObjectIterator other) const { return it > other.it; }
bool operator<=(ObjectIterator other) const { return it <= other.it; }
bool operator>=(ObjectIterator other) const { return it >= other.it; }
private:
ElementsIterator it;
}; };
class Object : public Base template<typename Element, typename ElementsIterator>
inline ObjectIterator<Element, ElementsIterator> operator+(
ObjectIterator<Element, ElementsIterator> a,
typename ObjectIterator<Element, ElementsIterator>::difference_type n)
{ {
public: return {a.elementsIterator() + 2 * n};
Entry *entryAt(int i) const { }
return reinterpret_cast<Entry *>(((char *)this) + table()[i]); template<typename Element, typename ElementsIterator>
} inline ObjectIterator<Element, ElementsIterator> operator+(
int indexOf(QStringView key, bool *exists) const; int n, ObjectIterator<Element, ElementsIterator> a)
int indexOf(QLatin1String key, bool *exists) const;
bool isValid(int maxSize) const;
};
class Array : public Base
{ {
public: return {a.elementsIterator() + 2 * n};
inline Value at(int i) const; }
inline Value &operator [](int i); template<typename Element, typename ElementsIterator>
inline ObjectIterator<Element, ElementsIterator> operator-(
ObjectIterator<Element, ElementsIterator> a,
typename ObjectIterator<Element, ElementsIterator>::difference_type n)
{
return {a.elementsIterator() - 2 * n};
}
template<typename Element, typename ElementsIterator>
inline int operator-(
ObjectIterator<Element, ElementsIterator> a,
ObjectIterator<Element, ElementsIterator> b)
{
return (a.elementsIterator() - b.elementsIterator()) / 2;
}
template<typename Element, typename ElementsIterator>
inline bool operator!=(
ObjectIterator<Element, ElementsIterator> a,
ObjectIterator<Element, ElementsIterator> b)
{
return a.elementsIterator() != b.elementsIterator();
}
template<typename Element, typename ElementsIterator>
inline bool operator==(
ObjectIterator<Element, ElementsIterator> a,
ObjectIterator<Element, ElementsIterator> b)
{
return a.elementsIterator() == b.elementsIterator();
}
bool isValid(int maxSize) const; using KeyIterator = ObjectIterator<QtCbor::Element, QVector<QtCbor::Element>::iterator>;
}; using ConstKeyIterator = ObjectIterator<const QtCbor::Element, QVector<QtCbor::Element>::const_iterator>;
template<>
inline KeyIterator::reference &KeyIterator::reference::operator=(const KeyIterator::value_type &value)
{
*m_key = value.key();
*(m_key + 1) = value.value();
return *this;
}
inline void swap(KeyIterator::reference a, KeyIterator::reference b)
{
KeyIterator::value_type t = a;
a = b;
b = t;
}
class Value class Value
{ {
public: public:
enum { static QCborContainerPrivate *container(const QCborValue &v) { return v.container; }
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; } static QJsonValue fromTrustedCbor(const QCborValue &v)
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); QJsonValue result;
return String((const char *)this + sizeof(Entry)); result.d = v.container;
result.n = v.n;
result.t = v.t;
return result;
} }
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 ==(QStringView key) const;
inline bool operator !=(QStringView key) const { return !operator ==(key); }
inline bool operator >=(QStringView 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 >=(QStringView key) const } // namespace QJsonPrivate
{
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() >= QString(key); // ### conversion to QString
}
inline bool operator <(QStringView 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(nullptr), 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.loadRelaxed() == 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 nullptr;
}
}
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_MOVE(Data)
};
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -40,12 +40,16 @@
#include <qjsonobject.h> #include <qjsonobject.h>
#include <qjsonvalue.h> #include <qjsonvalue.h>
#include <qjsonarray.h> #include <qjsonarray.h>
#include <qjsondocument.h>
#include <qstringlist.h> #include <qstringlist.h>
#include <qcborarray.h>
#include <qvariant.h> #include <qvariant.h>
#include <qdebug.h> #include <qdebug.h>
#include <private/qcborvalue_p.h>
#include <private/qjson_p.h>
#include "qjsonwriter_p.h" #include "qjsonwriter_p.h"
#include "qjson_p.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -131,10 +135,7 @@ QT_BEGIN_NAMESPACE
/*! /*!
Creates an empty array. Creates an empty array.
*/ */
QJsonArray::QJsonArray() QJsonArray::QJsonArray() = default;
: d(nullptr), a(nullptr)
{
}
/*! /*!
\fn QJsonArray::QJsonArray(std::initializer_list<QJsonValue> args) \fn QJsonArray::QJsonArray(std::initializer_list<QJsonValue> args)
@ -151,12 +152,10 @@ QJsonArray::QJsonArray()
/*! /*!
\internal \internal
*/ */
QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) QJsonArray::QJsonArray(QCborContainerPrivate *array)
: d(data), a(array) : a(array)
{ {
Q_ASSERT(data);
Q_ASSERT(array); Q_ASSERT(array);
d->ref.ref();
} }
/*! /*!
@ -168,18 +167,13 @@ QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array)
*/ */
void QJsonArray::initialize() void QJsonArray::initialize()
{ {
d = nullptr;
a = nullptr; a = nullptr;
} }
/*! /*!
Deletes the array. Deletes the array.
*/ */
QJsonArray::~QJsonArray() QJsonArray::~QJsonArray() = default;
{
if (d && !d->ref.deref())
delete d;
}
/*! /*!
Creates a copy of \a other. Creates a copy of \a other.
@ -187,12 +181,22 @@ QJsonArray::~QJsonArray()
Since QJsonArray is implicitly shared, the copy is shallow Since QJsonArray is implicitly shared, the copy is shallow
as long as the object doesn't get modified. as long as the object doesn't get modified.
*/ */
QJsonArray::QJsonArray(std::initializer_list<QJsonValue> args)
{
initialize();
for (const auto & arg : args)
append(arg);
}
QJsonArray::QJsonArray(const QJsonArray &other) QJsonArray::QJsonArray(const QJsonArray &other)
{ {
d = other.d;
a = other.a; a = other.a;
if (d) }
d->ref.ref();
QJsonArray::QJsonArray(QJsonArray &&other) noexcept
: a(other.a)
{
other.a = nullptr;
} }
/*! /*!
@ -200,15 +204,7 @@ QJsonArray::QJsonArray(const QJsonArray &other)
*/ */
QJsonArray &QJsonArray::operator =(const QJsonArray &other) 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; a = other.a;
return *this; return *this;
} }
@ -282,48 +278,7 @@ QJsonArray QJsonArray::fromStringList(const QStringList &list)
*/ */
QJsonArray QJsonArray::fromVariantList(const QVariantList &list) QJsonArray QJsonArray::fromVariantList(const QVariantList &list)
{ {
QJsonArray array; return QCborArray::fromVariantList(list).toJsonArray();
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(static_cast<void *>(array.a->table()),
static_cast<const void *>(values.constData()), values.size()*sizeof(uint));
array.a->length = values.size();
array.a->size = currentOffset + sizeof(QJsonPrivate::offset)*values.size();
return array;
} }
/*! /*!
@ -333,14 +288,7 @@ QJsonArray QJsonArray::fromVariantList(const QVariantList &list)
*/ */
QVariantList QJsonArray::toVariantList() const QVariantList QJsonArray::toVariantList() const
{ {
QVariantList list; return QCborArray::fromJsonArray(*this).toVariantList();
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;
} }
@ -349,10 +297,7 @@ QVariantList QJsonArray::toVariantList() const
*/ */
int QJsonArray::size() const int QJsonArray::size() const
{ {
if (!d) return a ? a->elements.size() : 0;
return 0;
return (int)a->length;
} }
/*! /*!
@ -370,10 +315,7 @@ int QJsonArray::size() const
*/ */
bool QJsonArray::isEmpty() const bool QJsonArray::isEmpty() const
{ {
if (!d) return a == nullptr || a->elements.isEmpty();
return true;
return !a->length;
} }
/*! /*!
@ -384,10 +326,10 @@ bool QJsonArray::isEmpty() const
*/ */
QJsonValue QJsonArray::at(int i) const QJsonValue QJsonArray::at(int i) const
{ {
if (!a || i < 0 || i >= (int)a->length) if (!a || i < 0 || i >= a->elements.size())
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
return QJsonValue(d, a, a->at(i)); return QJsonPrivate::Value::fromTrustedCbor(a->valueAt(i));
} }
/*! /*!
@ -411,7 +353,7 @@ QJsonValue QJsonArray::first() const
*/ */
QJsonValue QJsonArray::last() const QJsonValue QJsonArray::last() const
{ {
return at(a ? (a->length - 1) : 0); return at(a ? (a->elements.size() - 1) : 0);
} }
/*! /*!
@ -433,7 +375,7 @@ void QJsonArray::prepend(const QJsonValue &value)
*/ */
void QJsonArray::append(const QJsonValue &value) void QJsonArray::append(const QJsonValue &value)
{ {
insert(a ? (int)a->length : 0, value); insert(a ? a->elements.size() : 0, value);
} }
/*! /*!
@ -444,14 +386,10 @@ void QJsonArray::append(const QJsonValue &value)
*/ */
void QJsonArray::removeAt(int i) void QJsonArray::removeAt(int i)
{ {
if (!a || i < 0 || i >= (int)a->length) if (!a || i < 0 || i >= a->elements.length())
return; return;
detach2(); detach2();
a->removeItems(i, 1); a->removeAt(i);
++d->compactionCounter;
if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u)
compact();
} }
/*! \fn void QJsonArray::removeFirst() /*! \fn void QJsonArray::removeFirst()
@ -484,11 +422,12 @@ void QJsonArray::removeAt(int i)
*/ */
QJsonValue QJsonArray::takeAt(int i) QJsonValue QJsonArray::takeAt(int i)
{ {
if (!a || i < 0 || i >= (int)a->length) if (!a || i < 0 || i >= a->elements.length())
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
QJsonValue v(d, a, a->at(i)); detach2();
removeAt(i); // detaches const QJsonValue v = QJsonPrivate::Value::fromTrustedCbor(a->extractAt(i));
a->removeAt(i);
return v; return v;
} }
@ -501,29 +440,14 @@ QJsonValue QJsonArray::takeAt(int i)
*/ */
void QJsonArray::insert(int i, const QJsonValue &value) void QJsonArray::insert(int i, const QJsonValue &value)
{ {
Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0)); if (a)
QJsonValue val = value; detach2(a->elements.length() + 1);
else
a = new QCborContainerPrivate;
bool compressed; Q_ASSERT (i >= 0 && i <= a->elements.length());
int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); a->insertAt(i, value.type() == QJsonValue::Undefined ? QCborValue(nullptr)
: QCborValue::fromJsonValue(value));
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);
} }
/*! /*!
@ -552,33 +476,9 @@ void QJsonArray::insert(int i, const QJsonValue &value)
*/ */
void QJsonArray::replace(int i, const QJsonValue &value) void QJsonArray::replace(int i, const QJsonValue &value)
{ {
Q_ASSERT (a && i >= 0 && i < (int)(a->length)); Q_ASSERT (a && i >= 0 && i < a->elements.length());
QJsonValue val = value; detach2();
a->replaceAt(i, QCborValue::fromJsonValue(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();
} }
/*! /*!
@ -610,7 +510,7 @@ bool QJsonArray::contains(const QJsonValue &value) const
*/ */
QJsonValueRef QJsonArray::operator [](int i) QJsonValueRef QJsonArray::operator [](int i)
{ {
Q_ASSERT(a && i >= 0 && i < (int)a->length); Q_ASSERT(a && i >= 0 && i < a->elements.length());
return QJsonValueRef(this, i); return QJsonValueRef(this, i);
} }
@ -633,14 +533,14 @@ bool QJsonArray::operator==(const QJsonArray &other) const
return true; return true;
if (!a) if (!a)
return !other.a->length; return !other.a->elements.length();
if (!other.a) if (!other.a)
return !a->length; return !a->elements.length();
if (a->length != other.a->length) if (a->elements.length() != other.a->elements.length())
return false; return false;
for (int i = 0; i < (int)a->length; ++i) { for (int i = 0; i < a->elements.length(); ++i) {
if (QJsonValue(d, a, a->at(i)) != QJsonValue(other.d, other.a, other.a->at(i))) if (a->valueAt(i) != other.a->valueAt(i))
return false; return false;
} }
return true; return true;
@ -1216,28 +1116,10 @@ void QJsonArray::detach(uint reserve)
*/ */
bool QJsonArray::detach2(uint reserve) bool QJsonArray::detach2(uint reserve)
{ {
if (!d) { if (!a)
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; return true;
} a = a->detach(a.data(), reserve ? reserve : size());
if (reserve == 0 && d->ref.loadRelaxed() == 1) return a;
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;
} }
/*! /*!
@ -1245,12 +1127,7 @@ bool QJsonArray::detach2(uint reserve)
*/ */
void QJsonArray::compact() void QJsonArray::compact()
{ {
if (!d || !d->compactionCounter) a->compact(a->elements.size());
return;
detach2();
d->compact();
a = static_cast<QJsonPrivate::Array *>(d->header->root());
} }
uint qHash(const QJsonArray &array, uint seed) uint qHash(const QJsonArray &array, uint seed)
@ -1267,7 +1144,7 @@ QDebug operator<<(QDebug dbg, const QJsonArray &a)
return dbg; return dbg;
} }
QByteArray json; QByteArray json;
QJsonPrivate::Writer::arrayToJson(a.a, json, 0, true); QJsonPrivate::Writer::arrayToJson(a.a.data(), json, 0, true);
dbg.nospace() << "QJsonArray(" dbg.nospace() << "QJsonArray("
<< json.constData() // print as utf-8 string without extra quotation marks << json.constData() // print as utf-8 string without extra quotation marks
<< ")"; << ")";

View File

@ -42,6 +42,7 @@
#include <QtCore/qjsonvalue.h> #include <QtCore/qjsonvalue.h>
#include <QtCore/qiterator.h> #include <QtCore/qiterator.h>
#include <QtCore/qshareddata.h>
#include <initializer_list> #include <initializer_list>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -55,25 +56,14 @@ class Q_CORE_EXPORT QJsonArray
public: public:
QJsonArray(); QJsonArray();
QJsonArray(std::initializer_list<QJsonValue> args) QJsonArray(std::initializer_list<QJsonValue> args);
{
initialize();
for (std::initializer_list<QJsonValue>::const_iterator i = args.begin(); i != args.end(); ++i)
append(*i);
}
~QJsonArray(); ~QJsonArray();
QJsonArray(const QJsonArray &other); QJsonArray(const QJsonArray &other);
QJsonArray &operator =(const QJsonArray &other); QJsonArray &operator =(const QJsonArray &other);
QJsonArray(QJsonArray &&other) noexcept QJsonArray(QJsonArray &&other) noexcept;
: d(other.d),
a(other.a)
{
other.d = nullptr;
other.a = nullptr;
}
QJsonArray &operator =(QJsonArray &&other) noexcept QJsonArray &operator =(QJsonArray &&other) noexcept
{ {
@ -112,7 +102,6 @@ public:
void swap(QJsonArray &other) noexcept void swap(QJsonArray &other) noexcept
{ {
qSwap(d, other.d);
qSwap(a, other.a); qSwap(a, other.a);
} }
@ -244,20 +233,21 @@ public:
typedef int difference_type; typedef int difference_type;
private: private:
friend class QJsonPrivate::Data;
friend class QJsonValue; friend class QJsonValue;
friend class QJsonDocument; friend class QJsonDocument;
friend class QCborArray;
friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &);
QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array); QJsonArray(QCborContainerPrivate *array);
void initialize(); void initialize();
void compact(); void compact();
// ### Qt 6: remove me and merge with detach2 // ### Qt 6: remove me and merge with detach2
void detach(uint reserve = 0); void detach(uint reserve = 0);
bool detach2(uint reserve = 0); bool detach2(uint reserve = 0);
QJsonPrivate::Data *d; // ### Qt 6: remove
QJsonPrivate::Array *a; void *dead = nullptr;
QExplicitlySharedDataPointer<QCborContainerPrivate> a;
}; };
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray) Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray)

View File

@ -42,6 +42,10 @@
#include "qcborarray.h" #include "qcborarray.h"
#include "qcbormap.h" #include "qcbormap.h"
#include "qjsonarray.h"
#include "qjsonobject.h"
#include "qjsondocument.h"
#include "qjson_p.h" #include "qjson_p.h"
#include <private/qnumeric_p.h> #include <private/qnumeric_p.h>
@ -149,7 +153,12 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety
case QCborValue::Array: case QCborValue::Array:
case QCborValue::Map: case QCborValue::Map:
#if defined(QT_JSON_READONLY) || defined(QT_BOOTSTRAPPED)
qFatal("Writing JSON is disabled.");
return QString();
#else
return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact); return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact);
#endif
case QCborValue::SimpleType: case QCborValue::SimpleType:
break; break;
@ -181,30 +190,11 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety
return simpleTypeString(e.type); return simpleTypeString(e.type);
} }
static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx); QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx);
static QJsonArray convertToJsonArray(const QCborContainerPrivate *d) static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
{
QJsonArray a;
if (d) {
for (qsizetype idx = 0; idx < d->elements.size(); ++idx)
a.append(convertToJson(d, idx));
}
return a;
}
static QJsonObject convertToJsonObject(const QCborContainerPrivate *d)
{
QJsonObject o;
if (d) {
for (qsizetype idx = 0; idx < d->elements.size(); idx += 2)
o.insert(makeString(d, idx), convertToJson(d, idx + 1));
}
return o;
}
static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d)
{ {
#ifndef QT_BUILD_QMAKE
qint64 tag = d->elements.at(0).value; qint64 tag = d->elements.at(0).value;
switch (tag) { switch (tag) {
@ -225,12 +215,36 @@ static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d)
return s; return s;
} }
} }
#endif
// for all other tags, ignore it and return the converted tagged item // for all other tags, ignore it and return the converted tagged item
return convertToJson(d, 1); return qt_convertToJson(d, 1);
} }
static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) // We need to do this because sub-objects may need conversion.
static QJsonArray convertToJsonArray(QCborContainerPrivate *d)
{
QJsonArray a;
if (d) {
for (qsizetype idx = 0; idx < d->elements.size(); ++idx)
a.append(qt_convertToJson(d, idx));
}
return a;
}
// We need to do this because the keys need to be sorted and converted to strings
// and sub-objects may need recursive conversion.
static QJsonObject convertToJsonObject(QCborContainerPrivate *d)
{
QJsonObject o;
if (d) {
for (qsizetype idx = 0; idx < d->elements.size(); idx += 2)
o.insert(makeString(d, idx), qt_convertToJson(d, idx + 1));
}
return o;
}
QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx)
{ {
// encoding the container itself // encoding the container itself
if (idx == -QCborValue::Array) if (idx == -QCborValue::Array)
@ -248,7 +262,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
const auto &e = d->elements.at(idx); const auto &e = d->elements.at(idx);
switch (e.type) { switch (e.type) {
case QCborValue::Integer: case QCborValue::Integer:
return qint64(e.value); return QJsonPrivate::Value::fromTrustedCbor(e.value);
case QCborValue::ByteArray: case QCborValue::ByteArray:
case QCborValue::String: case QCborValue::String:
@ -264,14 +278,14 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
case QCborValue::RegularExpression: case QCborValue::RegularExpression:
case QCborValue::Uuid: case QCborValue::Uuid:
// recurse // recurse
return convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type); return qt_convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type);
case QCborValue::Null: case QCborValue::Null:
return QJsonValue(); return QJsonValue();
case QCborValue::Undefined: case QCborValue::Undefined:
case QCborValue::Invalid: case QCborValue::Invalid:
return QJsonValue(QJsonValue::Undefined); return QJsonValue::Undefined;
case QCborValue::False: case QCborValue::False:
return false; return false;
@ -283,7 +297,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
return fpToJson(e.fpvalue()); return fpToJson(e.fpvalue());
} }
return makeString(d, idx); return QJsonPrivate::Value::fromTrustedCbor(makeString(d, idx));
} }
/*! /*!
@ -348,22 +362,22 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
QJsonValue QCborValue::toJsonValue() const QJsonValue QCborValue::toJsonValue() const
{ {
if (container) if (container)
return convertToJson(container, n < 0 ? -type() : n); return qt_convertToJson(container, n < 0 ? -type() : n);
// simple values // simple values
switch (type()) { switch (type()) {
case Integer:
return n;
case Null:
return QJsonValue();
case False: case False:
return false; return false;
case Integer:
return QJsonPrivate::Value::fromTrustedCbor(n);
case True: case True:
return true; return true;
case Null:
return QJsonValue();
case Double: case Double:
return fpToJson(fp_helper()); return fpToJson(fp_helper());
@ -372,12 +386,12 @@ QJsonValue QCborValue::toJsonValue() const
case Undefined: case Undefined:
case Invalid: case Invalid:
return QJsonValue(QJsonValue::Undefined); return QJsonValue::Undefined;
case ByteArray: case ByteArray:
case String: case String:
// empty strings // empty strings
return QString(); return QJsonValue::String;
case Array: case Array:
// empty array // empty array
@ -392,16 +406,16 @@ QJsonValue QCborValue::toJsonValue() const
case Url: case Url:
case RegularExpression: case RegularExpression:
case Uuid: case Uuid:
Q_UNREACHABLE(); // Reachable, but invalid in Json
return QJsonValue::Undefined; return QJsonValue::Undefined;
} }
return simpleTypeString(type()); return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type()));
} }
QJsonValue QCborValueRef::toJsonValue() const QJsonValue QCborValueRef::toJsonValue() const
{ {
return convertToJson(d, i); return qt_convertToJson(d, i);
} }
/*! /*!
@ -540,8 +554,10 @@ QVariant QCborValue::toVariant() const
case DateTime: case DateTime:
return toDateTime(); return toDateTime();
#ifndef QT_BOOTSTRAPPED
case Url: case Url:
return toUrl(); return toUrl();
#endif
#if QT_CONFIG(regularexpression) #if QT_CONFIG(regularexpression)
case RegularExpression: case RegularExpression:
@ -597,12 +613,13 @@ QCborValue QCborValue::fromJsonValue(const QJsonValue &v)
{ {
switch (v.type()) { switch (v.type()) {
case QJsonValue::Bool: case QJsonValue::Bool:
return v.b; return v.toBool();
case QJsonValue::Double: { case QJsonValue::Double: {
qint64 i; qint64 i;
if (convertDoubleTo(v.dbl, &i)) const double dbl = v.toDouble();
if (convertDoubleTo(dbl, &i))
return i; return i;
return v.dbl; return dbl;
} }
case QJsonValue::String: case QJsonValue::String:
return v.toString(); return v.toString();
@ -710,8 +727,10 @@ QCborValue QCborValue::fromVariant(const QVariant &variant)
return variant.toByteArray(); return variant.toByteArray();
case QVariant::DateTime: case QVariant::DateTime:
return QCborValue(variant.toDateTime()); return QCborValue(variant.toDateTime());
#ifndef QT_BOOTSTRAPPED
case QVariant::Url: case QVariant::Url:
return QCborValue(variant.toUrl()); return QCborValue(variant.toUrl());
#endif
case QVariant::Uuid: case QVariant::Uuid:
return QCborValue(variant.toUuid()); return QCborValue(variant.toUuid());
case QVariant::List: case QVariant::List:
@ -824,15 +843,9 @@ QCborArray QCborArray::fromVariantList(const QVariantList &list)
*/ */
QCborArray QCborArray::fromJsonArray(const QJsonArray &array) QCborArray QCborArray::fromJsonArray(const QJsonArray &array)
{ {
QCborArray a; QCborArray result;
a.detach(array.size()); result.d = array.a;
for (const QJsonValue &v : array) { return result;
if (v.isString())
a.d->append(v.toString());
else
a.d->append(QCborValue::fromJsonValue(v));
}
return a;
} }
/*! /*!
@ -944,20 +957,9 @@ QCborMap QCborMap::fromVariantHash(const QVariantHash &hash)
*/ */
QCborMap QCborMap::fromJsonObject(const QJsonObject &obj) QCborMap QCborMap::fromJsonObject(const QJsonObject &obj)
{ {
QCborMap m; QCborMap result;
m.detach(obj.size()); result.d = obj.o;
QCborContainerPrivate *d = m.d.data(); return result;
auto it = obj.begin();
auto end = obj.end();
for ( ; it != end; ++it) {
d->append(it.key());
if (it.value().isString())
d->append(it.value().toString());
else
d->append(QCborValue::fromJsonValue(it.value()));
}
return m;
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -44,11 +44,22 @@
#include <qstringlist.h> #include <qstringlist.h>
#include <qvariant.h> #include <qvariant.h>
#include <qdebug.h> #include <qdebug.h>
#include <qcbormap.h>
#include <qcborarray.h>
#include "qcborvalue_p.h"
#include "qjsonwriter_p.h" #include "qjsonwriter_p.h"
#include "qjsonparser_p.h" #include "qjsonparser_p.h"
#include "qjson_p.h" #include "qjson_p.h"
#include "qdatastream.h" #include "qdatastream.h"
#if QT_CONFIG(binaryjson)
#include "qbinaryjson_p.h"
#include "qbinaryjsonobject_p.h"
#include "qbinaryjsonarray_p.h"
#endif
#include <private/qmemory_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
/*! \class QJsonDocument /*! \class QJsonDocument
@ -80,6 +91,33 @@ QT_BEGIN_NAMESPACE
\sa {JSON Support in Qt}, {JSON Save Game Example} \sa {JSON Support in Qt}, {JSON Save Game Example}
*/ */
class QJsonDocumentPrivate
{
Q_DISABLE_COPY_MOVE(QJsonDocumentPrivate);
public:
QJsonDocumentPrivate() = default;
QJsonDocumentPrivate(QCborValue data) : value(std::move(data)) {}
~QJsonDocumentPrivate()
{
if (rawData)
free(rawData);
}
QCborValue value;
char *rawData = nullptr;
uint rawDataSize = 0;
void clearRawData()
{
if (rawData) {
free(rawData);
rawData = nullptr;
rawDataSize = 0;
}
}
};
/*! /*!
* Constructs an empty and invalid document. * Constructs an empty and invalid document.
*/ */
@ -109,11 +147,10 @@ QJsonDocument::QJsonDocument(const QJsonArray &array)
/*! /*!
\internal \internal
*/ */
QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) QJsonDocument::QJsonDocument(const QCborValue &data)
: d(data) : d(qt_make_unique<QJsonDocumentPrivate>(data))
{ {
Q_ASSERT(d); Q_ASSERT(d);
d->ref.ref();
} }
/*! /*!
@ -121,20 +158,30 @@ QJsonDocument::QJsonDocument(QJsonPrivate::Data *data)
Binary data set with fromRawData is not freed. Binary data set with fromRawData is not freed.
*/ */
QJsonDocument::~QJsonDocument() QJsonDocument::~QJsonDocument() = default;
{
if (d && !d->ref.deref())
delete d;
}
/*! /*!
* Creates a copy of the \a other document. * Creates a copy of the \a other document.
*/ */
QJsonDocument::QJsonDocument(const QJsonDocument &other) QJsonDocument::QJsonDocument(const QJsonDocument &other)
{ {
d = other.d; if (other.d) {
if (d) if (!d)
d->ref.ref(); d = qt_make_unique<QJsonDocumentPrivate>();
d->value = other.d->value;
} else {
d.reset();
}
}
QJsonDocument::QJsonDocument(QJsonDocument &&other) noexcept
: d(std::move(other.d))
{
}
void QJsonDocument::swap(QJsonDocument &other) noexcept
{
qSwap(d, other.d);
} }
/*! /*!
@ -143,14 +190,17 @@ QJsonDocument::QJsonDocument(const QJsonDocument &other)
*/ */
QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
{ {
if (d != other.d) { if (this != &other) {
if (d && !d->ref.deref()) if (other.d) {
delete d; if (!d)
d = other.d; d = qt_make_unique<QJsonDocumentPrivate>();
if (d) else
d->ref.ref(); d->clearRawData();
d->value = other.d->value;
} else {
d.reset();
}
} }
return *this; return *this;
} }
@ -187,12 +237,13 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
the application. the application.
*/ */
#if QT_CONFIG(binaryjson)
/*! /*!
Creates a QJsonDocument that uses the first \a size bytes from Creates a QJsonDocument that uses the first \a size bytes from
\a data. It assumes \a data contains a binary encoded JSON document. \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 The created document does not take ownership of \a data. The data is
has to guarantee that \a data will not be deleted or modified as long as copied into a different data structure, and the original data can be
any QJsonDocument, QJsonObject or QJsonArray still references the data. deleted or modified afterwards.
\a data has to be aligned to a 4 byte boundary. \a data has to be aligned to a 4 byte boundary.
@ -202,7 +253,18 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
Returns a QJsonDocument representing the data. Returns a QJsonDocument representing the data.
\sa rawData(), fromBinaryData(), isNull(), DataValidation \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
compatibility. It is undocumented and restrictive in the maximum size of JSON
documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
which can in turn be serialized into the CBOR binary format and vice versa. The
CBOR format is a well-defined and less restrictive binary representation for a
superset of JSON.
\note Before Qt 5.15, the caller had to guarantee that \a data would not be
deleted or modified as long as any QJsonDocument, QJsonObject or QJsonArray
still referenced the data. From Qt 5.15 on, this is not necessary anymore.
\sa rawData(), fromBinaryData(), isNull(), DataValidation, QCborValue
*/ */
QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation)
{ {
@ -211,18 +273,15 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat
return QJsonDocument(); return QJsonDocument();
} }
if (size < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) if (size < 0 || uint(size) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base))
return QJsonDocument(); return QJsonDocument();
QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size); std::unique_ptr<QBinaryJsonPrivate::ConstData> binaryData
d->ownsData = false; = qt_make_unique<QBinaryJsonPrivate::ConstData>(data, size);
if (validation != BypassValidation && !d->valid()) { return (validation == BypassValidation || binaryData->isValid())
delete d; ? binaryData->toJsonDocument()
return QJsonDocument(); : QJsonDocument();
}
return QJsonDocument(d);
} }
/*! /*!
@ -230,7 +289,16 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat
\a size will contain the size of the returned data. \a size will contain the size of the returned data.
This method is useful to e.g. stream the JSON document This method is useful to e.g. stream the JSON document
in it's binary form to a file. in its binary form to a file.
\deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
compatibility. It is undocumented and restrictive in the maximum size of JSON
documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
which can in turn be serialized into the CBOR binary format and vice versa. The
CBOR format is a well-defined and less restrictive binary representation for a
superset of JSON.
\sa QCborValue
*/ */
const char *QJsonDocument::rawData(int *size) const const char *QJsonDocument::rawData(int *size) const
{ {
@ -238,7 +306,21 @@ const char *QJsonDocument::rawData(int *size) const
*size = 0; *size = 0;
return nullptr; return nullptr;
} }
*size = d->alloc;
if (!d->rawData) {
if (isObject()) {
QBinaryJsonObject o = QBinaryJsonObject::fromJsonObject(object());
d->rawData = o.takeRawData(&(d->rawDataSize));
} else {
QBinaryJsonArray a = QBinaryJsonArray::fromJsonArray(array());
d->rawData = a.takeRawData(&(d->rawDataSize));
}
}
// It would be quite miraculous if not, as we should have hit the 128MB limit then.
Q_ASSERT(d->rawDataSize <= uint(std::numeric_limits<int>::max()));
*size = d->rawDataSize;
return d->rawData; return d->rawData;
} }
@ -249,39 +331,65 @@ const char *QJsonDocument::rawData(int *size) const
By default the data is validated. If the \a data is not valid, the method returns By default the data is validated. If the \a data is not valid, the method returns
a null document. a null document.
\sa toBinaryData(), fromRawData(), isNull(), DataValidation \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
compatibility. It is undocumented and restrictive in the maximum size of JSON
documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
which can in turn be serialized into the CBOR binary format and vice versa. The
CBOR format is a well-defined and less restrictive binary representation for a
superset of JSON.
\sa toBinaryData(), fromRawData(), isNull(), DataValidation, QCborValue
*/ */
QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation)
{ {
if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) if (uint(data.size()) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base))
return QJsonDocument(); return QJsonDocument();
QJsonPrivate::Header h; QBinaryJsonPrivate::Header h;
memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header)); memcpy(&h, data.constData(), sizeof(QBinaryJsonPrivate::Header));
QJsonPrivate::Base root; QBinaryJsonPrivate::Base root;
memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base)); memcpy(&root, data.constData() + sizeof(QBinaryJsonPrivate::Header),
sizeof(QBinaryJsonPrivate::Base));
// do basic checks here, so we don't try to allocate more memory than we can. const uint size = sizeof(QBinaryJsonPrivate::Header) + root.size;
if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u || if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1U || size > uint(data.size()))
sizeof(QJsonPrivate::Header) + root.size > (uint)data.size())
return QJsonDocument(); return QJsonDocument();
const uint size = sizeof(QJsonPrivate::Header) + root.size; std::unique_ptr<QBinaryJsonPrivate::ConstData> d
char *raw = (char *)malloc(size); = qt_make_unique<QBinaryJsonPrivate::ConstData>(data.constData(), size);
if (!raw)
return QJsonDocument();
memcpy(raw, data.constData(), size); return (validation == BypassValidation || d->isValid())
QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size); ? d->toJsonDocument()
: QJsonDocument();
if (validation != BypassValidation && !d->valid()) {
delete d;
return QJsonDocument();
}
return QJsonDocument(d);
} }
/*!
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.
\deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
compatibility. It is undocumented and restrictive in the maximum size of JSON
documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
which can in turn be serialized into the CBOR binary format and vice versa. The
CBOR format is a well-defined and less restrictive binary representation for a
superset of JSON.
\sa fromBinaryData(), QCborValue
*/
QByteArray QJsonDocument::toBinaryData() const
{
int size = 0;
const char *raw = rawData(&size);
return QByteArray(raw, size);
}
#endif // QT_CONFIG(binaryjson)
/*! /*!
Creates a QJsonDocument from the QVariant \a variant. Creates a QJsonDocument from the QVariant \a variant.
@ -293,6 +401,7 @@ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidati
QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
{ {
QJsonDocument doc; QJsonDocument doc;
switch (variant.type()) { switch (variant.type()) {
case QVariant::Map: case QVariant::Map:
doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); doc.setObject(QJsonObject::fromVariantMap(variant.toMap()));
@ -304,7 +413,8 @@ QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
doc.setArray(QJsonArray::fromVariantList(variant.toList())); doc.setArray(QJsonArray::fromVariantList(variant.toList()));
break; break;
case QVariant::StringList: case QVariant::StringList:
doc.setArray(QJsonArray::fromStringList(variant.toStringList())); doc.d = qt_make_unique<QJsonDocumentPrivate>();
doc.d->value = QCborArray::fromStringList(variant.toStringList());
break; break;
default: default:
break; break;
@ -325,10 +435,10 @@ QVariant QJsonDocument::toVariant() const
if (!d) if (!d)
return QVariant(); return QVariant();
if (d->header->root()->isArray()) QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root())).toVariantList(); if (d->value.isArray())
else return QJsonArray(container).toVariantList();
return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root())).toVariantMap(); return QJsonObject(container).toVariantMap();
} }
/*! /*!
@ -370,10 +480,11 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const
if (!d) if (!d)
return json; return json;
if (d->header->root()->isArray()) const QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(d->header->root()), json, 0, (format == Compact)); if (d->value.isArray())
QJsonPrivate::Writer::arrayToJson(container, json, 0, (format == Compact));
else else
QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(d->header->root()), json, 0, (format == Compact)); QJsonPrivate::Writer::objectToJson(container, json, 0, (format == Compact));
return json; return json;
} }
@ -392,7 +503,13 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const
QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
{ {
QJsonPrivate::Parser parser(json.constData(), json.length()); QJsonPrivate::Parser parser(json.constData(), json.length());
return parser.parse(error); QJsonDocument result;
const QCborValue val = parser.parse(error);
if (val.isArray() || val.isMap()) {
result.d = qt_make_unique<QJsonDocumentPrivate>();
result.d->value = val;
}
return result;
} }
/*! /*!
@ -406,26 +523,6 @@ bool QJsonDocument::isEmpty() const
return false; 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. Returns \c true if the document contains an array.
@ -436,8 +533,7 @@ bool QJsonDocument::isArray() const
if (!d) if (!d)
return false; return false;
QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; return d->value.isArray();
return h->root()->isArray();
} }
/*! /*!
@ -450,8 +546,7 @@ bool QJsonDocument::isObject() const
if (!d) if (!d)
return false; return false;
QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; return d->value.isMap();
return h->root()->isObject();
} }
/*! /*!
@ -464,10 +559,9 @@ bool QJsonDocument::isObject() const
*/ */
QJsonObject QJsonDocument::object() const QJsonObject QJsonDocument::object() const
{ {
if (d) { if (isObject()) {
QJsonPrivate::Base *b = d->header->root(); if (auto container = QJsonPrivate::Value::container(d->value))
if (b->isObject()) return QJsonObject(container);
return QJsonObject(d, static_cast<QJsonPrivate::Object *>(b));
} }
return QJsonObject(); return QJsonObject();
} }
@ -482,10 +576,9 @@ QJsonObject QJsonDocument::object() const
*/ */
QJsonArray QJsonDocument::array() const QJsonArray QJsonDocument::array() const
{ {
if (d) { if (isArray()) {
QJsonPrivate::Base *b = d->header->root(); if (auto container = QJsonPrivate::Value::container(d->value))
if (b->isArray()) return QJsonArray(container);
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(b));
} }
return QJsonArray(); return QJsonArray();
} }
@ -497,24 +590,12 @@ QJsonArray QJsonDocument::array() const
*/ */
void QJsonDocument::setObject(const QJsonObject &object) void QJsonDocument::setObject(const QJsonObject &object)
{ {
if (d && !d->ref.deref()) if (!d)
delete d; d = qt_make_unique<QJsonDocumentPrivate>();
else
d->clearRawData();
d = object.d; d->value = QCborValue::fromJsonValue(object);
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();
} }
/*! /*!
@ -524,24 +605,12 @@ void QJsonDocument::setObject(const QJsonObject &object)
*/ */
void QJsonDocument::setArray(const QJsonArray &array) void QJsonDocument::setArray(const QJsonArray &array)
{ {
if (d && !d->ref.deref()) if (!d)
delete d; d = qt_make_unique<QJsonDocumentPrivate>();
else
d->clearRawData();
d = array.d; d->value = QCborValue::fromJsonValue(array);
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();
} }
#if QT_STRINGVIEW_LEVEL < 2 #if QT_STRINGVIEW_LEVEL < 2
@ -572,7 +641,7 @@ const QJsonValue QJsonDocument::operator[](QStringView key) const
if (!isObject()) if (!isObject())
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
return object().value(key); return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key));
} }
/*! /*!
@ -584,7 +653,7 @@ const QJsonValue QJsonDocument::operator[](QLatin1String key) const
if (!isObject()) if (!isObject())
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
return object().value(key); return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key));
} }
/*! /*!
@ -604,7 +673,7 @@ const QJsonValue QJsonDocument::operator[](int i) const
if (!isArray()) if (!isArray())
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
return array().at(i); return QJsonPrivate::Value::fromTrustedCbor(d->value.toArray().at(i));
} }
/*! /*!
@ -612,21 +681,7 @@ const QJsonValue QJsonDocument::operator[](int i) const
*/ */
bool QJsonDocument::operator==(const QJsonDocument &other) const bool QJsonDocument::operator==(const QJsonDocument &other) const
{ {
if (d == other.d) return (!d) ? (!other.d) : (d->value == other.d->value);
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()));
} }
/*! /*!
@ -658,10 +713,11 @@ QDebug operator<<(QDebug dbg, const QJsonDocument &o)
return dbg; return dbg;
} }
QByteArray json; QByteArray json;
if (o.d->header->root()->isArray()) const QCborContainerPrivate *container = QJsonPrivate::Value::container(o.d->value);
QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(o.d->header->root()), json, 0, true); if (o.d->value.isArray())
QJsonPrivate::Writer::arrayToJson(container, json, 0, true);
else else
QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(o.d->header->root()), json, 0, true); QJsonPrivate::Writer::objectToJson(container, json, 0, true);
dbg.nospace() << "QJsonDocument(" dbg.nospace() << "QJsonDocument("
<< json.constData() // print as utf-8 string without extra quotation marks << json.constData() // print as utf-8 string without extra quotation marks
<< ')'; << ')';

View File

@ -41,14 +41,16 @@
#define QJSONDOCUMENT_H #define QJSONDOCUMENT_H
#include <QtCore/qjsonvalue.h> #include <QtCore/qjsonvalue.h>
#include <QtCore/qscopedpointer.h>
#include <memory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDebug; class QDebug;
class QCborValue;
namespace QJsonPrivate { namespace QJsonPrivate { class Parser; }
class Parser;
}
struct Q_CORE_EXPORT QJsonParseError struct Q_CORE_EXPORT QJsonParseError
{ {
@ -76,6 +78,7 @@ struct Q_CORE_EXPORT QJsonParseError
ParseError error; ParseError error;
}; };
class QJsonDocumentPrivate;
class Q_CORE_EXPORT QJsonDocument class Q_CORE_EXPORT QJsonDocument
{ {
public: public:
@ -93,11 +96,7 @@ public:
QJsonDocument(const QJsonDocument &other); QJsonDocument(const QJsonDocument &other);
QJsonDocument &operator =(const QJsonDocument &other); QJsonDocument &operator =(const QJsonDocument &other);
QJsonDocument(QJsonDocument &&other) noexcept QJsonDocument(QJsonDocument &&other) noexcept;
: d(other.d)
{
other.d = nullptr;
}
QJsonDocument &operator =(QJsonDocument &&other) noexcept QJsonDocument &operator =(QJsonDocument &&other) noexcept
{ {
@ -105,21 +104,20 @@ public:
return *this; return *this;
} }
void swap(QJsonDocument &other) noexcept void swap(QJsonDocument &other) noexcept;
{
qSwap(d, other.d);
}
enum DataValidation { enum DataValidation {
Validate, Validate,
BypassValidation BypassValidation
}; };
#if QT_CONFIG(binaryjson)
static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate);
const char *rawData(int *size) const; const char *rawData(int *size) const;
static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate);
QByteArray toBinaryData() const; QByteArray toBinaryData() const;
#endif // QT_CONFIG(binaryjson)
static QJsonDocument fromVariant(const QVariant &variant); static QJsonDocument fromVariant(const QVariant &variant);
QVariant toVariant() const; QVariant toVariant() const;
@ -160,13 +158,12 @@ public:
private: private:
friend class QJsonValue; friend class QJsonValue;
friend class QJsonPrivate::Data;
friend class QJsonPrivate::Parser; friend class QJsonPrivate::Parser;
friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &);
QJsonDocument(QJsonPrivate::Data *data); QJsonDocument(const QCborValue &data);
QJsonPrivate::Data *d; std::unique_ptr<QJsonDocumentPrivate> d;
}; };
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument) Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument)

View File

@ -40,11 +40,17 @@
#include <qjsonobject.h> #include <qjsonobject.h>
#include <qjsonvalue.h> #include <qjsonvalue.h>
#include <qjsonarray.h> #include <qjsonarray.h>
#include <qjsondocument.h>
#include <qstringlist.h> #include <qstringlist.h>
#include <qdebug.h> #include <qdebug.h>
#include <qvariant.h> #include <qvariant.h>
#include "qjson_p.h" #include <qcbormap.h>
#include <private/qcborvalue_p.h>
#include "qjsonwriter_p.h" #include "qjsonwriter_p.h"
#include "qjson_p.h"
#include <algorithm>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -109,10 +115,7 @@ QT_BEGIN_NAMESPACE
\sa isEmpty() \sa isEmpty()
*/ */
QJsonObject::QJsonObject() QJsonObject::QJsonObject() = default;
: d(nullptr), o(nullptr)
{
}
/*! /*!
\fn QJsonObject::QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args) \fn QJsonObject::QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args)
@ -131,12 +134,10 @@ QJsonObject::QJsonObject()
/*! /*!
\internal \internal
*/ */
QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) QJsonObject::QJsonObject(QCborContainerPrivate *object)
: d(data), o(object) : o(object)
{ {
Q_ASSERT(d);
Q_ASSERT(o); Q_ASSERT(o);
d->ref.ref();
} }
/*! /*!
@ -149,17 +150,19 @@ QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object)
void QJsonObject::initialize() void QJsonObject::initialize()
{ {
d = nullptr;
o = nullptr; o = nullptr;
} }
/*! /*!
Destroys the object. Destroys the object.
*/ */
QJsonObject::~QJsonObject() QJsonObject::~QJsonObject() = default;
QJsonObject::QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args)
{ {
if (d && !d->ref.deref()) initialize();
delete d; for (const auto &arg : args)
insert(arg.first, arg.second);
} }
/*! /*!
@ -170,10 +173,13 @@ QJsonObject::~QJsonObject()
*/ */
QJsonObject::QJsonObject(const QJsonObject &other) QJsonObject::QJsonObject(const QJsonObject &other)
{ {
d = other.d;
o = other.o; o = other.o;
if (d) }
d->ref.ref();
QJsonObject::QJsonObject(QJsonObject &&other) noexcept
: o(other.o)
{
other.o = nullptr;
} }
/*! /*!
@ -181,15 +187,7 @@ QJsonObject::QJsonObject(const QJsonObject &other)
*/ */
QJsonObject &QJsonObject::operator =(const QJsonObject &other) 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; o = other.o;
return *this; return *this;
} }
@ -225,55 +223,7 @@ QJsonObject &QJsonObject::operator =(const QJsonObject &other)
*/ */
QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map)
{ {
QJsonObject object; return QCborMap::fromVariantMap(map).toJsonObject();
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;
} }
/*! /*!
@ -285,14 +235,7 @@ QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map)
*/ */
QVariantMap QJsonObject::toVariantMap() const QVariantMap QJsonObject::toVariantMap() const
{ {
QVariantMap map; return QCborMap::fromJsonObject(*this).toVariantMap();
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;
} }
/*! /*!
@ -324,15 +267,7 @@ QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash)
*/ */
QVariantHash QJsonObject::toVariantHash() const QVariantHash QJsonObject::toVariantHash() const
{ {
QVariantHash hash; return QCborMap::fromJsonObject(*this).toVariantHash();
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;
} }
/*! /*!
@ -344,11 +279,9 @@ QStringList QJsonObject::keys() const
{ {
QStringList keys; QStringList keys;
if (o) { if (o) {
keys.reserve(o->length); keys.reserve(o->elements.length() / 2);
for (uint i = 0; i < o->length; ++i) { for (int i = 0, end = o->elements.length(); i < end; i += 2)
QJsonPrivate::Entry *e = o->entryAt(i); keys.append(o->stringAt(i));
keys.append(e->key());
}
} }
return keys; return keys;
} }
@ -358,10 +291,7 @@ QStringList QJsonObject::keys() const
*/ */
int QJsonObject::size() const int QJsonObject::size() const
{ {
if (!d) return o ? o->elements.length() / 2 : 0;
return 0;
return o->length;
} }
/*! /*!
@ -371,10 +301,24 @@ int QJsonObject::size() const
*/ */
bool QJsonObject::isEmpty() const bool QJsonObject::isEmpty() const
{ {
if (!d) return !o || o->elements.isEmpty();
return true; }
return !o->length; template<typename String>
static int indexOf(const QExplicitlySharedDataPointer<QCborContainerPrivate> &o,
String key, bool *keyExists)
{
const auto begin = QJsonPrivate::ConstKeyIterator(o->elements.constBegin());
const auto end = QJsonPrivate::ConstKeyIterator(o->elements.constEnd());
const auto it = std::lower_bound(
begin, end, key,
[&](const QJsonPrivate::ConstKeyIterator::value_type &e, const String &key) {
return o->stringCompareElement(e.key(), key) < 0;
});
*keyExists = (it != end) && o->stringEqualsElement((*it).key(), key);
return (it - begin) * 2;
} }
#if QT_STRINGVIEW_LEVEL < 2 #if QT_STRINGVIEW_LEVEL < 2
@ -415,14 +359,14 @@ QJsonValue QJsonObject::value(QLatin1String key) const
template <typename T> template <typename T>
QJsonValue QJsonObject::valueImpl(T key) const QJsonValue QJsonObject::valueImpl(T key) const
{ {
if (!d) if (!o)
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
bool keyExists; bool keyExists;
int i = o->indexOf(key, &keyExists); int i = indexOf(o, key, &keyExists);
if (!keyExists) if (!keyExists)
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
return QJsonValue(d, o, o->entryAt(i)->value); return QJsonPrivate::Value::fromTrustedCbor(o->valueAt(i + 1));
} }
#if QT_STRINGVIEW_LEVEL < 2 #if QT_STRINGVIEW_LEVEL < 2
@ -497,13 +441,16 @@ QJsonValueRef QJsonObject::operator [](QLatin1String key)
template <typename T> template <typename T>
QJsonValueRef QJsonObject::atImpl(T key) QJsonValueRef QJsonObject::atImpl(T key)
{ {
if (!o)
o = new QCborContainerPrivate;
bool keyExists = false; bool keyExists = false;
int index = o ? o->indexOf(key, &keyExists) : 0; int index = indexOf(o, key, &keyExists);
if (!keyExists) { if (!keyExists) {
iterator i = insertAt(index, key, QJsonValue(), false); o->insertAt(index, key);
index = i.i; o->insertAt(index + 1, QCborValue::fromJsonValue(QJsonValue()));
} }
return QJsonValueRef(this, index); return QJsonValueRef(this, index / 2);
} }
#if QT_STRINGVIEW_LEVEL < 2 #if QT_STRINGVIEW_LEVEL < 2
@ -550,12 +497,12 @@ QJsonObject::iterator QJsonObject::insert(QLatin1String key, const QJsonValue &v
template <typename T> template <typename T>
QJsonObject::iterator QJsonObject::insertImpl(T key, const QJsonValue &value) QJsonObject::iterator QJsonObject::insertImpl(T key, const QJsonValue &value)
{ {
if (value.t == QJsonValue::Undefined) { if (value.type() == QJsonValue::Undefined) {
remove(key); remove(key);
return end(); return end();
} }
bool keyExists = false; bool keyExists = false;
int pos = o ? o->indexOf(key, &keyExists) : 0; int pos = o ? indexOf(o, key, &keyExists) : 0;
return insertAt(pos, key, value, keyExists); return insertAt(pos, key, value, keyExists);
} }
@ -565,40 +512,18 @@ QJsonObject::iterator QJsonObject::insertImpl(T key, const QJsonValue &value)
template <typename T> template <typename T>
QJsonObject::iterator QJsonObject::insertAt(int pos, T key, const QJsonValue &value, bool keyExists) QJsonObject::iterator QJsonObject::insertAt(int pos, T key, const QJsonValue &value, bool keyExists)
{ {
QJsonValue val = value; if (o)
detach2(o->elements.length() / 2 + (keyExists ? 0 : 1));
else
o = new QCborContainerPrivate;
bool latinOrIntValue; if (keyExists) {
int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); o->replaceAt(pos + 1, QCborValue::fromJsonValue(value));
} else {
bool latinKey = QJsonPrivate::useCompressed(key); o->insertAt(pos, key);
int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); o->insertAt(pos + 1, QCborValue::fromJsonValue(value));
int requiredSize = valueOffset + valueSize; }
return {this, pos / 2};
if (!detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry
return iterator();
if (!o->length)
o->tableOffset = sizeof(QJsonPrivate::Object);
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);
compactIfNeeded();
return iterator(this, pos);
} }
#if QT_STRINGVIEW_LEVEL < 2 #if QT_STRINGVIEW_LEVEL < 2
@ -637,11 +562,11 @@ void QJsonObject::remove(QLatin1String key)
template <typename T> template <typename T>
void QJsonObject::removeImpl(T key) void QJsonObject::removeImpl(T key)
{ {
if (!d) if (!o)
return; return;
bool keyExists; bool keyExists;
int index = o->indexOf(key, &keyExists); int index = indexOf(o, key, &keyExists);
if (!keyExists) if (!keyExists)
return; return;
@ -692,13 +617,12 @@ QJsonValue QJsonObject::takeImpl(T key)
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
bool keyExists; bool keyExists;
int index = o->indexOf(key, &keyExists); int index = indexOf(o, key, &keyExists);
if (!keyExists) if (!keyExists)
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
QJsonValue v(d, o, o->entryAt(index)->value); const QJsonValue v = QJsonPrivate::Value::fromTrustedCbor(o->extractAt(index + 1));
removeAt(index); removeAt(index);
return v; return v;
} }
@ -742,7 +666,7 @@ bool QJsonObject::containsImpl(T key) const
return false; return false;
bool keyExists; bool keyExists;
o->indexOf(key, &keyExists); indexOf(o, key, &keyExists);
return keyExists; return keyExists;
} }
@ -755,16 +679,14 @@ bool QJsonObject::operator==(const QJsonObject &other) const
return true; return true;
if (!o) if (!o)
return !other.o->length; return !other.o->elements.length();
if (!other.o) if (!other.o)
return !o->length; return !o->elements.length();
if (o->length != other.o->length) if (o->elements.length() != other.o->elements.length())
return false; return false;
for (uint i = 0; i < o->length; ++i) { for (int i = 0, end = o->elements.length(); i < end; ++i) {
QJsonPrivate::Entry *e = o->entryAt(i); if (o->valueAt(i) != other.o->valueAt(i))
QJsonValue v(d, o, e->value);
if (other.value(e->key()) != v)
return false; return false;
} }
@ -788,9 +710,8 @@ bool QJsonObject::operator!=(const QJsonObject &other) const
*/ */
QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it) QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it)
{ {
Q_ASSERT(d && d->ref.loadRelaxed() == 1); if (it.o != this || it.i < 0 || it.i >= o->elements.length())
if (it.o != this || it.i < 0 || it.i >= (int)o->length) return {this, o->elements.length()};
return iterator(this, o->length);
int index = it.i; int index = it.i;
@ -839,11 +760,11 @@ template <typename T>
QJsonObject::iterator QJsonObject::findImpl(T key) QJsonObject::iterator QJsonObject::findImpl(T key)
{ {
bool keyExists = false; bool keyExists = false;
int index = o ? o->indexOf(key, &keyExists) : 0; int index = o ? indexOf(o, key, &keyExists) : 0;
if (!keyExists) if (!keyExists)
return end(); return end();
detach2(); detach2();
return iterator(this, index); return {this, index / 2};
} }
#if QT_STRINGVIEW_LEVEL < 2 #if QT_STRINGVIEW_LEVEL < 2
@ -904,10 +825,10 @@ template <typename T>
QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
{ {
bool keyExists = false; bool keyExists = false;
int index = o ? o->indexOf(key, &keyExists) : 0; int index = o ? indexOf(o, key, &keyExists) : 0;
if (!keyExists) if (!keyExists)
return end(); return end();
return const_iterator(this, index); return {this, index / 2};
} }
/*! \fn int QJsonObject::count() const /*! \fn int QJsonObject::count() const
@ -1500,28 +1421,10 @@ void QJsonObject::detach(uint reserve)
bool QJsonObject::detach2(uint reserve) bool QJsonObject::detach2(uint reserve)
{ {
if (!d) { if (!o)
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; return true;
} o = QCborContainerPrivate::detach(o.data(), reserve ? reserve * 2 : o->elements.length());
if (reserve == 0 && d->ref.loadRelaxed() == 1) return o;
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;
} }
/*! /*!
@ -1529,21 +1432,11 @@ bool QJsonObject::detach2(uint reserve)
*/ */
void QJsonObject::compact() void QJsonObject::compact()
{ {
if (!d || !d->compactionCounter) if (!o)
return; return;
detach2(); detach2();
d->compact(); o->compact(o->elements.length());
o = static_cast<QJsonPrivate::Object *>(d->header->root());
}
/*!
\internal
*/
void QJsonObject::compactIfNeeded()
{
if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u)
compact();
} }
/*! /*!
@ -1551,10 +1444,8 @@ void QJsonObject::compactIfNeeded()
*/ */
QString QJsonObject::keyAt(int i) const QString QJsonObject::keyAt(int i) const
{ {
Q_ASSERT(o && i >= 0 && i < (int)o->length); Q_ASSERT(o && i >= 0 && i * 2 < o->elements.length());
return o->stringAt(i * 2);
QJsonPrivate::Entry *e = o->entryAt(i);
return e->key();
} }
/*! /*!
@ -1562,11 +1453,9 @@ QString QJsonObject::keyAt(int i) const
*/ */
QJsonValue QJsonObject::valueAt(int i) const QJsonValue QJsonObject::valueAt(int i) const
{ {
if (!o || i < 0 || i >= (int)o->length) if (!o || i < 0 || 2 * i + 1 >= o->elements.length())
return QJsonValue(QJsonValue::Undefined); return QJsonValue(QJsonValue::Undefined);
return QJsonPrivate::Value::fromTrustedCbor(o->valueAt(2 * i + 1));
QJsonPrivate::Entry *e = o->entryAt(i);
return QJsonValue(d, o, e->value);
} }
/*! /*!
@ -1574,13 +1463,13 @@ QJsonValue QJsonObject::valueAt(int i) const
*/ */
void QJsonObject::setValueAt(int i, const QJsonValue &val) void QJsonObject::setValueAt(int i, const QJsonValue &val)
{ {
Q_ASSERT(o && i >= 0 && i < (int)o->length); Q_ASSERT(o && i >= 0 && 2 * i + 1 < o->elements.length());
if (val.isUndefined()) {
QJsonPrivate::Entry *e = o->entryAt(i); o->removeAt(2 * i + 1);
if (val.t == QJsonValue::Undefined) o->removeAt(2 * i);
removeAt(i); } else {
else o->replaceAt(2 * i + 1, QCborValue::fromJsonValue(val));
insertAt(i, e->key(), val, true); }
} }
/*! /*!
@ -1589,9 +1478,8 @@ void QJsonObject::setValueAt(int i, const QJsonValue &val)
void QJsonObject::removeAt(int index) void QJsonObject::removeAt(int index)
{ {
detach2(); detach2();
o->removeItems(index, 1); o->removeAt(index + 1);
++d->compactionCounter; o->removeAt(index);
compactIfNeeded();
} }
uint qHash(const QJsonObject &object, uint seed) uint qHash(const QJsonObject &object, uint seed)
@ -1614,7 +1502,7 @@ QDebug operator<<(QDebug dbg, const QJsonObject &o)
return dbg; return dbg;
} }
QByteArray json; QByteArray json;
QJsonPrivate::Writer::objectToJson(o.o, json, 0, true); QJsonPrivate::Writer::objectToJson(o.o.data(), json, 0, true);
dbg.nospace() << "QJsonObject(" dbg.nospace() << "QJsonObject("
<< json.constData() // print as utf-8 string without extra quotation marks << json.constData() // print as utf-8 string without extra quotation marks
<< ")"; << ")";

View File

@ -43,6 +43,7 @@
#include <QtCore/qjsonvalue.h> #include <QtCore/qjsonvalue.h>
#include <QtCore/qiterator.h> #include <QtCore/qiterator.h>
#include <QtCore/qpair.h> #include <QtCore/qpair.h>
#include <QtCore/qshareddata.h>
#include <initializer_list> #include <initializer_list>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -53,29 +54,21 @@ typedef QMap<QString, QVariant> QVariantMap;
template <class Key, class T> class QHash; template <class Key, class T> class QHash;
typedef QHash<QString, QVariant> QVariantHash; typedef QHash<QString, QVariant> QVariantHash;
class QCborContainerPrivate;
class Q_CORE_EXPORT QJsonObject class Q_CORE_EXPORT QJsonObject
{ {
public: public:
QJsonObject(); QJsonObject();
QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args) 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);
}
~QJsonObject(); ~QJsonObject();
QJsonObject(const QJsonObject &other); QJsonObject(const QJsonObject &other);
QJsonObject &operator =(const QJsonObject &other); QJsonObject &operator =(const QJsonObject &other);
QJsonObject(QJsonObject &&other) noexcept QJsonObject(QJsonObject &&other) noexcept;
: d(other.d), o(other.o)
{
other.d = nullptr;
other.o = nullptr;
}
QJsonObject &operator =(QJsonObject &&other) noexcept QJsonObject &operator =(QJsonObject &&other) noexcept
{ {
@ -85,7 +78,6 @@ public:
void swap(QJsonObject &other) noexcept void swap(QJsonObject &other) noexcept
{ {
qSwap(d, other.d);
qSwap(o, other.o); qSwap(o, other.o);
} }
@ -275,20 +267,18 @@ public:
inline bool empty() const { return isEmpty(); } inline bool empty() const { return isEmpty(); }
private: private:
friend class QJsonPrivate::Data;
friend class QJsonValue; friend class QJsonValue;
friend class QJsonDocument; friend class QJsonDocument;
friend class QJsonValueRef; friend class QJsonValueRef;
friend class QCborMap;
friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &);
QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object); QJsonObject(QCborContainerPrivate *object);
void initialize(); void initialize();
// ### Qt 6: remove me and merge with detach2 // ### Qt 6: remove me and merge with detach2
void detach(uint reserve = 0); void detach(uint reserve = 0);
bool detach2(uint reserve = 0); bool detach2(uint reserve = 0);
void compact(); void compact();
void compactIfNeeded();
template <typename T> QJsonValue valueImpl(T key) const; template <typename T> QJsonValue valueImpl(T key) const;
template <typename T> QJsonValueRef atImpl(T key); template <typename T> QJsonValueRef atImpl(T key);
@ -305,8 +295,9 @@ private:
void removeAt(int i); void removeAt(int i);
template <typename T> iterator insertAt(int i, T key, const QJsonValue &val, bool exists); template <typename T> iterator insertAt(int i, T key, const QJsonValue &val, bool exists);
QJsonPrivate::Data *d; // ### Qt 6: remove
QJsonPrivate::Object *o; void *dead = nullptr;
QExplicitlySharedDataPointer<QCborContainerPrivate> o;
}; };
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject) Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject)

View File

@ -45,6 +45,8 @@
#include "qjsonparser_p.h" #include "qjsonparser_p.h"
#include "qjson_p.h" #include "qjson_p.h"
#include "private/qutfcodec_p.h" #include "private/qutfcodec_p.h"
#include "private/qcborvalue_p.h"
#include "private/qnumeric_p.h"
//#define PARSER_DEBUG //#define PARSER_DEBUG
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
@ -197,9 +199,32 @@ QString QJsonParseError::errorString() const
using namespace QJsonPrivate; using namespace QJsonPrivate;
class StashedContainer
{
Q_DISABLE_COPY_MOVE(StashedContainer)
public:
StashedContainer(QExplicitlySharedDataPointer<QCborContainerPrivate> *container,
QCborValue::Type type)
: type(type), stashed(std::move(*container)), current(container)
{
}
~StashedContainer()
{
stashed->append(QCborContainerPrivate::makeValue(type, -1, current->take(),
QCborContainerPrivate::MoveContainer));
*current = std::move(stashed);
}
private:
QCborValue::Type type;
QExplicitlySharedDataPointer<QCborContainerPrivate> stashed;
QExplicitlySharedDataPointer<QCborContainerPrivate> *current;
};
Parser::Parser(const char *json, int length) Parser::Parser(const char *json, int length)
: head(json), json(json), data(nullptr) : head(json), json(json)
, dataLength(0), current(0), nestingLevel(0) , nestingLevel(0)
, lastError(QJsonParseError::NoError) , lastError(QJsonParseError::NoError)
{ {
end = json + length; end = json + length;
@ -297,34 +322,30 @@ char Parser::nextToken()
/* /*
JSON-text = object / array JSON-text = object / array
*/ */
QJsonDocument Parser::parse(QJsonParseError *error) QCborValue Parser::parse(QJsonParseError *error)
{ {
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
indent = 0; indent = 0;
qDebug(">>>>> parser begin"); qDebug(">>>>> parser begin");
#endif #endif
// allocate some space
dataLength = qMax(end - json, (ptrdiff_t) 256);
data = (char *)malloc(dataLength);
Q_CHECK_PTR(data);
// fill in Header data
QJsonPrivate::Header *h = (QJsonPrivate::Header *)data;
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1u;
current = sizeof(QJsonPrivate::Header);
eatBOM(); eatBOM();
char token = nextToken(); char token = nextToken();
QCborValue data;
DEBUG << Qt::hex << (uint)token; DEBUG << Qt::hex << (uint)token;
if (token == BeginArray) { if (token == BeginArray) {
container = new QCborContainerPrivate;
if (!parseArray()) if (!parseArray())
goto error; goto error;
data = QCborContainerPrivate::makeValue(QCborValue::Array, -1, container.take(),
QCborContainerPrivate::MoveContainer);
} else if (token == BeginObject) { } else if (token == BeginObject) {
container = new QCborContainerPrivate;
if (!parseObject()) if (!parseObject())
goto error; goto error;
data = QCborContainerPrivate::makeValue(QCborValue::Map, -1, container.take(),
QCborContainerPrivate::MoveContainer);
} else { } else {
lastError = QJsonParseError::IllegalValue; lastError = QJsonParseError::IllegalValue;
goto error; goto error;
@ -342,44 +363,95 @@ QJsonDocument Parser::parse(QJsonParseError *error)
error->offset = 0; error->offset = 0;
error->error = QJsonParseError::NoError; error->error = QJsonParseError::NoError;
} }
QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current);
return QJsonDocument(d); return data;
} }
error: error:
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
qDebug(">>>>> parser error"); qDebug(">>>>> parser error");
#endif #endif
container.reset();
if (error) { if (error) {
error->offset = json - head; error->offset = json - head;
error->error = lastError; error->error = lastError;
} }
free(data); return QCborValue();
return QJsonDocument();
} }
void Parser::ParsedObject::insert(uint offset) {
const QJsonPrivate::Entry *newEntry = reinterpret_cast<const QJsonPrivate::Entry *>(parser->data + objectPosition + offset); static void sortContainer(QCborContainerPrivate *container)
int min = 0; {
int n = offsets.size(); using Forward = QJsonPrivate::KeyIterator;
while (n > 0) { using Reverse = std::reverse_iterator<Forward>;
int half = n >> 1; using Value = Forward::value_type;
int middle = min + half;
if (*entryAt(middle) >= *newEntry) { auto compare = [container](const Value &a, const Value &b)
n = half; {
} else { const auto &aKey = a.key();
min = middle + 1; const auto &bKey = b.key();
n -= half + 1;
Q_ASSERT(aKey.flags & QtCbor::Element::HasByteData);
Q_ASSERT(bKey.flags & QtCbor::Element::HasByteData);
const QtCbor::ByteData *aData = container->byteData(aKey);
const QtCbor::ByteData *bData = container->byteData(bKey);
if (!aData)
return bData ? -1 : 0;
if (!bData)
return 1;
// If StringIsAscii is set, we can use either the UTF-8 or the latin1 comparison
// for the string as ASCII is a subset of both. If nothing is set, that means UTF-8.
// We are currently missing an efficient comparison between UTF-8 and UTF-16 strings.
// Therefore, we need to convert the UTF-8 string if we encounter such a case.
if (aKey.flags & QtCbor::Element::StringIsAscii) {
if (bKey.flags & QtCbor::Element::StringIsAscii)
return QtPrivate::compareStrings(aData->asLatin1(), bData->asLatin1());
if (bKey.flags & QtCbor::Element::StringIsUtf16)
return QtPrivate::compareStrings(aData->asLatin1(), bData->asStringView());
return QCborContainerPrivate::compareUtf8(aData, bData->asLatin1());
} }
}
if (min < offsets.size() && *entryAt(min) == *newEntry) { if (aKey.flags & QtCbor::Element::StringIsUtf16) {
offsets[min] = offset; if (bKey.flags & QtCbor::Element::StringIsAscii)
} else { return QtPrivate::compareStrings(aData->asStringView(), bData->asLatin1());
offsets.insert(min, offset); if (bKey.flags & QtCbor::Element::StringIsUtf16)
} return QtPrivate::compareStrings(aData->asStringView(), bData->asStringView());
// Nasty case. a is UTF-16 and b is UTF-8
return QtPrivate::compareStrings(aData->asStringView(), bData->toUtf8String());
}
if (bKey.flags & QtCbor::Element::StringIsAscii)
return QCborContainerPrivate::compareUtf8(aData, bData->asLatin1());
// Nasty case. a is UTF-8 and b is UTF-16
if (bKey.flags & QtCbor::Element::StringIsUtf16)
return QtPrivate::compareStrings(aData->toUtf8String(), bData->asStringView());
return QCborContainerPrivate::compareUtf8(aData, bData->asLatin1());
};
std::sort(Forward(container->elements.begin()), Forward(container->elements.end()),
[&compare](const Value &a, const Value &b) { return compare(a, b) < 0; });
// We need to retain the _last_ value for any duplicate keys. Therefore the reverse dance here.
auto it = std::unique(Reverse(container->elements.end()), Reverse(container->elements.begin()),
[&compare](const Value &a, const Value &b) {
return compare(a, b) == 0;
}).base().elementsIterator();
// The erase from beginning is expensive but hopefully rare.
container->elements.erase(container->elements.begin(), it);
} }
/* /*
object = begin-object [ member *( value-separator member ) ] object = begin-object [ member *( value-separator member ) ]
end-object end-object
@ -392,19 +464,14 @@ bool Parser::parseObject()
return false; return false;
} }
int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object)); BEGIN << "parseObject" << json;
if (objectOffset < 0)
return false;
BEGIN << "parseObject pos=" << objectOffset << current << json;
ParsedObject parsedObject(this, objectOffset);
char token = nextToken(); char token = nextToken();
while (token == Quote) { while (token == Quote) {
int off = current - objectOffset; if (!container)
if (!parseMember(objectOffset)) container = new QCborContainerPrivate;
if (!parseMember())
return false; return false;
parsedObject.insert(off);
token = nextToken(); token = nextToken();
if (token != ValueSeparator) if (token != ValueSeparator)
break; break;
@ -421,50 +488,23 @@ bool Parser::parseObject()
return false; 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; END;
--nestingLevel; --nestingLevel;
if (container)
sortContainer(container.data());
return true; return true;
} }
/* /*
member = string name-separator value member = string name-separator value
*/ */
bool Parser::parseMember(int baseOffset) bool Parser::parseMember()
{ {
int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry)); BEGIN << "parseMember";
if (entryOffset < 0)
return false;
BEGIN << "parseMember pos=" << entryOffset;
bool latin1; if (!parseString())
if (!parseString(&latin1))
return false; return false;
char token = nextToken(); char token = nextToken();
if (token != NameSeparator) { if (token != NameSeparator) {
@ -475,56 +515,13 @@ bool Parser::parseMember(int baseOffset)
lastError = QJsonParseError::UnterminatedObject; lastError = QJsonParseError::UnterminatedObject;
return false; return false;
} }
QJsonPrivate::Value val; if (!parseValue())
if (!parseValue(&val, baseOffset))
return false; return false;
// finalize the entry
QJsonPrivate::Entry *e = (QJsonPrivate::Entry *)(data + entryOffset);
e->value = val;
e->value.latinKey = latin1;
END; END;
return true; 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 array = begin-array [ value *( value-separator value ) ] end-array
*/ */
@ -537,12 +534,6 @@ bool Parser::parseArray()
return false; return false;
} }
int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array));
if (arrayOffset < 0)
return false;
ValueArray values;
if (!eatSpace()) { if (!eatSpace()) {
lastError = QJsonParseError::UnterminatedArray; lastError = QJsonParseError::UnterminatedArray;
return false; return false;
@ -555,13 +546,10 @@ bool Parser::parseArray()
lastError = QJsonParseError::UnterminatedArray; lastError = QJsonParseError::UnterminatedArray;
return false; return false;
} }
QJsonPrivate::Value val; if (!container)
if (!parseValue(&val, arrayOffset)) container = new QCborContainerPrivate;
if (!parseValue())
return false; return false;
if (!values.append(val)) {
lastError = QJsonParseError::DocumentTooLarge;
return false;
}
char token = nextToken(); char token = nextToken();
if (token == EndArray) if (token == EndArray)
break; break;
@ -575,27 +563,11 @@ bool Parser::parseArray()
} }
} }
DEBUG << "size =" << values.size; DEBUG << "size =" << (container ? container->elements.length() : 0);
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; END;
--nestingLevel; --nestingLevel;
return true; return true;
} }
@ -604,10 +576,9 @@ value = false / null / true / object / array / number / string
*/ */
bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) bool Parser::parseValue()
{ {
BEGIN << "parse Value" << json; BEGIN << "parse Value" << json;
val->_dummy = 0;
switch (*json++) { switch (*json++) {
case 'n': case 'n':
@ -618,7 +589,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
if (*json++ == 'u' && if (*json++ == 'u' &&
*json++ == 'l' && *json++ == 'l' &&
*json++ == 'l') { *json++ == 'l') {
val->type = QJsonValue::Null; container->append(QCborValue(QCborValue::Null));
DEBUG << "value: null"; DEBUG << "value: null";
END; END;
return true; return true;
@ -633,8 +604,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
if (*json++ == 'r' && if (*json++ == 'r' &&
*json++ == 'u' && *json++ == 'u' &&
*json++ == 'e') { *json++ == 'e') {
val->type = QJsonValue::Bool; container->append(QCborValue(true));
val->value = true;
DEBUG << "value: true"; DEBUG << "value: true";
END; END;
return true; return true;
@ -650,8 +620,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
*json++ == 'l' && *json++ == 'l' &&
*json++ == 's' && *json++ == 's' &&
*json++ == 'e') { *json++ == 'e') {
val->type = QJsonValue::Bool; container->append(QCborValue(false));
val->value = false;
DEBUG << "value: false"; DEBUG << "value: false";
END; END;
return true; return true;
@ -659,44 +628,28 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
lastError = QJsonParseError::IllegalValue; lastError = QJsonParseError::IllegalValue;
return false; return false;
case Quote: { case Quote: {
val->type = QJsonValue::String; if (!parseString())
if (current - baseOffset >= Value::MaxSize) {
lastError = QJsonParseError::DocumentTooLarge;
return false; return false;
}
val->value = current - baseOffset;
bool latin1;
if (!parseString(&latin1))
return false;
val->latinOrIntValue = latin1;
DEBUG << "value: string"; DEBUG << "value: string";
END; END;
return true; return true;
} }
case BeginArray: case BeginArray: {
val->type = QJsonValue::Array; StashedContainer stashedContainer(&container, QCborValue::Array);
if (current - baseOffset >= Value::MaxSize) {
lastError = QJsonParseError::DocumentTooLarge;
return false;
}
val->value = current - baseOffset;
if (!parseArray()) if (!parseArray())
return false; return false;
DEBUG << "value: array"; DEBUG << "value: array";
END; END;
return true; return true;
case BeginObject: }
val->type = QJsonValue::Object; case BeginObject: {
if (current - baseOffset >= Value::MaxSize) { StashedContainer stashedContainer(&container, QCborValue::Map);
lastError = QJsonParseError::DocumentTooLarge;
return false;
}
val->value = current - baseOffset;
if (!parseObject()) if (!parseObject())
return false; return false;
DEBUG << "value: object"; DEBUG << "value: object";
END; END;
return true; return true;
}
case ValueSeparator: case ValueSeparator:
// Essentially missing value, but after a colon, not after a comma // Essentially missing value, but after a colon, not after a comma
// like the other MissingObject errors. // like the other MissingObject errors.
@ -708,7 +661,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
return false; return false;
default: default:
--json; --json;
if (!parseNumber(val, baseOffset)) if (!parseNumber())
return false; return false;
DEBUG << "value: number"; DEBUG << "value: number";
END; END;
@ -735,10 +688,9 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
*/ */
bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) bool Parser::parseNumber()
{ {
BEGIN << "parseNumber" << json; BEGIN << "parseNumber" << json;
val->type = QJsonValue::Double;
const char *start = json; const char *start = json;
bool isInt = true; bool isInt = true;
@ -778,42 +730,32 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
return false; return false;
} }
QByteArray number(start, json - start); const QByteArray number = QByteArray::fromRawData(start, json - start);
DEBUG << "numberstring" << number; DEBUG << "numberstring" << number;
if (isInt) { if (isInt) {
bool ok; bool ok;
int n = number.toInt(&ok); qlonglong n = number.toLongLong(&ok);
if (ok && n < (1<<25) && n > -(1<<25)) { if (ok) {
val->int_value = n; container->append(QCborValue(n));
val->latinOrIntValue = true;
END; END;
return true; return true;
} }
} }
bool ok; bool ok;
union { double d = number.toDouble(&ok);
quint64 ui;
double d;
};
d = number.toDouble(&ok);
if (!ok) { if (!ok) {
lastError = QJsonParseError::IllegalNumber; lastError = QJsonParseError::IllegalNumber;
return false; return false;
} }
int pos = reserveSpace(sizeof(double)); qint64 n;
if (pos < 0) if (convertDoubleTo(d, &n))
return false; container->append(QCborValue(n));
qToLittleEndian(ui, data + pos); else
if (current - baseOffset >= Value::MaxSize) { container->append(QCborValue(d));
lastError = QJsonParseError::DocumentTooLarge;
return false;
}
val->value = pos - baseOffset;
val->latinOrIntValue = false;
END; END;
return true; return true;
@ -902,58 +844,45 @@ static inline bool scanEscapeSequence(const char *&json, const char *end, uint *
static inline bool scanUtf8Char(const char *&json, const char *end, uint *result) static inline bool scanUtf8Char(const char *&json, const char *end, uint *result)
{ {
const uchar *&src = reinterpret_cast<const uchar *&>(json); const auto *usrc = reinterpret_cast<const uchar *>(json);
const uchar *uend = reinterpret_cast<const uchar *>(end); const auto *uend = reinterpret_cast<const uchar *>(end);
uchar b = *src++; const uchar b = *usrc++;
int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, result, src, uend); int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, result, usrc, uend);
if (res < 0) { if (res < 0)
// decoding error, backtrack the character we read above
--json;
return false; return false;
}
json = reinterpret_cast<const char *>(usrc);
return true; return true;
} }
bool Parser::parseString(bool *latin1) bool Parser::parseString()
{ {
*latin1 = true;
const char *start = json; const char *start = json;
int outStart = current;
// try to write out a latin1 string // try to parse a utf-8 string without escape sequences, and note whether it's 7bit ASCII.
int stringPos = reserveSpace(2); BEGIN << "parse string" << json;
if (stringPos < 0) bool isUtf8 = true;
return false; bool isAscii = true;
BEGIN << "parse string stringPos=" << stringPos << json;
while (json < end) { while (json < end) {
uint ch = 0; uint ch = 0;
if (*json == '"') if (*json == '"')
break; break;
else if (*json == '\\') { if (*json == '\\') {
if (!scanEscapeSequence(json, end, &ch)) { isAscii = false;
lastError = QJsonParseError::IllegalEscapeSequence; // If we find escape sequences, we store UTF-16 as there are some
return false; // escape sequences which are hard to represent in UTF-8.
} // (plain "\\ud800" for example)
} else { isUtf8 = false;
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; break;
} }
int pos = reserveSpace(1); if (!scanUtf8Char(json, end, &ch)) {
if (pos < 0) lastError = QJsonParseError::IllegalUTF8String;
return false; return false;
DEBUG << " " << ch << (char)ch; }
data[pos] = (uchar)ch; if (ch > 0x7f)
isAscii = false;
DEBUG << " " << ch << char(ch);
} }
++json; ++json;
DEBUG << "end of string"; DEBUG << "end of string";
@ -962,25 +891,20 @@ bool Parser::parseString(bool *latin1)
return false; return false;
} }
// no unicode string, we are done // no escape sequences, we are done
if (*latin1) { if (isUtf8) {
// write string length container->appendByteData(start, json - start - 1, QCborValue::String,
*(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort)); isAscii ? QtCbor::Element::StringIsAscii
int pos = reserveSpace((4 - current) & 3); : QtCbor::Element::ValueFlags {});
if (pos < 0)
return false;
while (pos & 3)
data[pos++] = 0;
END; END;
return true; return true;
} }
*latin1 = false; DEBUG << "has escape sequences";
DEBUG << "not latin";
json = start; json = start;
current = outStart + sizeof(int);
QString ucs4;
while (json < end) { while (json < end) {
uint ch = 0; uint ch = 0;
if (*json == '"') if (*json == '"')
@ -997,16 +921,10 @@ bool Parser::parseString(bool *latin1)
} }
} }
if (QChar::requiresSurrogates(ch)) { if (QChar::requiresSurrogates(ch)) {
int pos = reserveSpace(4); ucs4.append(QChar::highSurrogate(ch));
if (pos < 0) ucs4.append(QChar::lowSurrogate(ch));
return false;
*(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch);
*(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch);
} else { } else {
int pos = reserveSpace(2); ucs4.append(QChar(ushort(ch)));
if (pos < 0)
return false;
*(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch;
} }
} }
++json; ++json;
@ -1016,13 +934,8 @@ bool Parser::parseString(bool *latin1)
return false; return false;
} }
// write string length container->appendByteData(reinterpret_cast<const char *>(ucs4.utf16()), ucs4.size() * 2,
*(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2; QCborValue::String, QtCbor::Element::StringIsUtf16);
int pos = reserveSpace((4 - current) & 3);
if (pos < 0)
return false;
while (pos & 3)
data[pos++] = 0;
END; END;
return true; return true;
} }

View File

@ -52,8 +52,8 @@
// //
#include <QtCore/private/qglobal_p.h> #include <QtCore/private/qglobal_p.h>
#include <qjsondocument.h> #include <QtCore/private/qcborvalue_p.h>
#include <qvarlengtharray.h> #include <QtCore/qjsondocument.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -64,25 +64,7 @@ class Parser
public: public:
Parser(const char *json, int length); Parser(const char *json, int length);
QJsonDocument parse(QJsonParseError *error); QCborValue 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: private:
inline void eatBOM(); inline void eatBOM();
@ -91,34 +73,17 @@ private:
bool parseObject(); bool parseObject();
bool parseArray(); bool parseArray();
bool parseMember(int baseOffset); bool parseMember();
bool parseString(bool *latin1); bool parseString();
bool parseValue(QJsonPrivate::Value *val, int baseOffset); bool parseValue();
bool parseNumber(QJsonPrivate::Value *val, int baseOffset); bool parseNumber();
const char *head; const char *head;
const char *json; const char *json;
const char *end; const char *end;
char *data;
int dataLength;
int current;
int nestingLevel; int nestingLevel;
QJsonParseError::ParseError lastError; QJsonParseError::ParseError lastError;
QExplicitlySharedDataPointer<QCborContainerPrivate> container;
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;
}
}; };
} }

View File

@ -40,6 +40,7 @@
#include <qjsonobject.h> #include <qjsonobject.h>
#include <qjsonvalue.h> #include <qjsonvalue.h>
#include <qjsonarray.h> #include <qjsonarray.h>
#include <qjsondocument.h>
#include <qurl.h> #include <qurl.h>
#include <quuid.h> #include <quuid.h>
#include <qvariant.h> #include <qvariant.h>
@ -47,10 +48,11 @@
#include <qdebug.h> #include <qdebug.h>
#include "qdatastream.h" #include "qdatastream.h"
#ifndef QT_BOOTSTRAPPED #include <private/qnumeric_p.h>
# include <qcborarray.h> #include <private/qcborvalue_p.h>
# include <qcbormap.h>
#endif #include <qcborarray.h>
#include <qcbormap.h>
#include "qjson_p.h" #include "qjson_p.h"
@ -112,70 +114,61 @@ QT_BEGIN_NAMESPACE
The default is to create a Null value. The default is to create a Null value.
*/ */
QJsonValue::QJsonValue(Type type) QJsonValue::QJsonValue(Type type)
: ui(0), d(nullptr), t(type) : d(nullptr), t(QCborValue::Undefined)
{ {
} switch (type) {
/*!
\internal
*/
QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v)
: d(nullptr)
{
t = (Type)(uint)v.type;
switch (t) {
case Undefined:
case Null: case Null:
dbl = 0; t = QCborValue::Null;
break; break;
case Bool: case Bool:
b = v.toBoolean(); t = QCborValue::False;
break; break;
case Double: case Double:
dbl = v.toDouble(base); t = QCborValue::Double;
break; break;
case String: { case String:
QString s = v.toString(base); t = QCborValue::String;
stringData = s.data_ptr();
stringData->ref.ref();
break; break;
}
case Array: case Array:
t = QCborValue::Array;
break;
case Object: case Object:
d = data; t = QCborValue::Map;
this->base = v.base(base); break;
case Undefined:
break; break;
} }
if (d)
d->ref.ref();
} }
/*! /*!
Creates a value of type Bool, with value \a b. Creates a value of type Bool, with value \a b.
*/ */
QJsonValue::QJsonValue(bool b) QJsonValue::QJsonValue(bool b)
: d(nullptr), t(Bool) : t(b ? QCborValue::True : QCborValue::False)
{ {
this->b = b;
} }
/*! /*!
Creates a value of type Double, with value \a n. Creates a value of type Double, with value \a n.
*/ */
QJsonValue::QJsonValue(double n) QJsonValue::QJsonValue(double v)
: d(nullptr), t(Double) : d(nullptr)
{ {
this->dbl = n; if (convertDoubleTo(v, &n)) {
t = QCborValue::Integer;
} else {
memcpy(&n, &v, sizeof(n));
t = QCborValue::Double;
}
} }
/*! /*!
\overload \overload
Creates a value of type Double, with value \a n. Creates a value of type Double, with value \a n.
*/ */
QJsonValue::QJsonValue(int n) QJsonValue::QJsonValue(int v)
: d(nullptr), t(Double) : n(v), t(QCborValue::Integer)
{ {
this->dbl = n;
} }
/*! /*!
@ -184,19 +177,17 @@ QJsonValue::QJsonValue(int n)
NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992). 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. If you pass in values outside this range expect a loss of precision to occur.
*/ */
QJsonValue::QJsonValue(qint64 n) QJsonValue::QJsonValue(qint64 v)
: d(nullptr), t(Double) : n(v), t(QCborValue::Integer)
{ {
this->dbl = double(n);
} }
/*! /*!
Creates a value of type String, with value \a s. Creates a value of type String, with value \a s.
*/ */
QJsonValue::QJsonValue(const QString &s) QJsonValue::QJsonValue(const QString &s)
: d(nullptr), t(String) : QJsonValue(QJsonPrivate::Value::fromTrustedCbor(s))
{ {
stringDataFromQStringHelper(s);
} }
/*! /*!
@ -211,71 +202,50 @@ QJsonValue::QJsonValue(const QString &s)
\since 5.3 \since 5.3
*/ */
// ### Qt6: remove
void QJsonValue::stringDataFromQStringHelper(const QString &string) void QJsonValue::stringDataFromQStringHelper(const QString &string)
{ {
stringData = *(QStringData **)(&string); *this = QJsonValue(string);
stringData->ref.ref();
} }
/*! /*!
Creates a value of type String, with value \a s. Creates a value of type String, with value \a s.
*/ */
QJsonValue::QJsonValue(QLatin1String s) QJsonValue::QJsonValue(QLatin1String s)
: d(nullptr), t(String) : QJsonValue(QJsonPrivate::Value::fromTrustedCbor(s))
{ {
// ### FIXME: Avoid creating the temp QString below
QString str(s);
stringDataFromQStringHelper(str);
} }
/*! /*!
Creates a value of type Array, with value \a a. Creates a value of type Array, with value \a a.
*/ */
QJsonValue::QJsonValue(const QJsonArray &a) QJsonValue::QJsonValue(const QJsonArray &a)
: d(a.d), t(Array) : n(-1), d(a.a), t(QCborValue::Array)
{ {
base = a.a;
if (d)
d->ref.ref();
} }
/*! /*!
Creates a value of type Object, with value \a o. Creates a value of type Object, with value \a o.
*/ */
QJsonValue::QJsonValue(const QJsonObject &o) QJsonValue::QJsonValue(const QJsonObject &o)
: d(o.d), t(Object) : n(-1), d(o.o), t(QCborValue::Map)
{ {
base = o.o;
if (d)
d->ref.ref();
} }
/*! /*!
Destroys the value. Destroys the value.
*/ */
QJsonValue::~QJsonValue() QJsonValue::~QJsonValue() = default;
{
if (t == String && stringData && !stringData->ref.deref())
free(stringData);
if (d && !d->ref.deref())
delete d;
}
/*! /*!
Creates a copy of \a other. Creates a copy of \a other.
*/ */
QJsonValue::QJsonValue(const QJsonValue &other) QJsonValue::QJsonValue(const QJsonValue &other)
{ {
n = other.n;
t = other.t; t = other.t;
d = other.d; d = other.d;
ui = other.ui;
if (d)
d->ref.ref();
if (t == String && stringData)
stringData->ref.ref();
} }
/*! /*!
@ -288,6 +258,23 @@ QJsonValue &QJsonValue::operator =(const QJsonValue &other)
return *this; return *this;
} }
QJsonValue::QJsonValue(QJsonValue &&other) noexcept :
n(other.n),
d(other.d),
t(other.t)
{
other.n = 0;
other.d = nullptr;
other.t = QCborValue::Null;
}
void QJsonValue::swap(QJsonValue &other) noexcept
{
qSwap(n, other.n);
qSwap(d, other.d);
qSwap(t, other.t);
}
/*! /*!
\fn QJsonValue::QJsonValue(QJsonValue &&other) \fn QJsonValue::QJsonValue(QJsonValue &&other)
\since 5.10 \since 5.10
@ -528,23 +515,27 @@ QJsonValue QJsonValue::fromVariant(const QVariant &variant)
QVariant QJsonValue::toVariant() const QVariant QJsonValue::toVariant() const
{ {
switch (t) { switch (t) {
case Bool: case QCborValue::True:
return b; return true;
case Double: case QCborValue::False:
return dbl; return false;
case String: case QCborValue::Integer:
case QCborValue::Double:
return toDouble();
case QCborValue::String:
return toString(); return toString();
case Array: case QCborValue::Array:
return d ? return d ?
QJsonArray(d, static_cast<QJsonPrivate::Array *>(base)).toVariantList() : QJsonArray(d.data()).toVariantList() :
QVariantList(); QVariantList();
case Object: case QCborValue::Map:
return d ? return d ?
QJsonObject(d, static_cast<QJsonPrivate::Object *>(base)).toVariantMap() : QJsonObject(d.data()).toVariantMap() :
QVariantMap(); QVariantMap();
case Null: case QCborValue::Null:
return QVariant::fromValue(nullptr); return QVariant::fromValue(nullptr);
case Undefined: case QCborValue::Undefined:
default:
break; break;
} }
return QVariant(); return QVariant();
@ -573,7 +564,25 @@ QVariant QJsonValue::toVariant() const
*/ */
QJsonValue::Type QJsonValue::type() const QJsonValue::Type QJsonValue::type() const
{ {
return t; switch (t) {
case QCborValue::Null:
return QJsonValue::Null;
case QCborValue::True:
case QCborValue::False:
return QJsonValue::Bool;
case QCborValue::Double:
case QCborValue::Integer:
return QJsonValue::Double;
case QCborValue::String:
return QJsonValue::String;
case QCborValue::Array:
return QJsonValue::Array;
case QCborValue::Map:
return QJsonValue::Object;
case QCborValue::Undefined:
default:
return QJsonValue::Undefined;
}
} }
/*! /*!
@ -583,9 +592,14 @@ QJsonValue::Type QJsonValue::type() const
*/ */
bool QJsonValue::toBool(bool defaultValue) const bool QJsonValue::toBool(bool defaultValue) const
{ {
if (t != Bool) switch (t) {
case QCborValue::True:
return true;
case QCborValue::False:
return false;
default:
return defaultValue; return defaultValue;
return b; }
} }
/*! /*!
@ -597,9 +611,20 @@ bool QJsonValue::toBool(bool defaultValue) const
*/ */
int QJsonValue::toInt(int defaultValue) const int QJsonValue::toInt(int defaultValue) const
{ {
if (t == Double && int(dbl) == dbl) switch (t) {
return int(dbl); case QCborValue::Double: {
return defaultValue; const double dbl = toDouble();
int dblInt;
convertDoubleTo<int>(dbl, &dblInt);
return dbl == dblInt ? dblInt : defaultValue;
}
case QCborValue::Integer:
return (n <= qint64(std::numeric_limits<int>::max())
&& n >= qint64(std::numeric_limits<int>::min()))
? n : defaultValue;
default:
return defaultValue;
}
} }
/*! /*!
@ -609,9 +634,17 @@ int QJsonValue::toInt(int defaultValue) const
*/ */
double QJsonValue::toDouble(double defaultValue) const double QJsonValue::toDouble(double defaultValue) const
{ {
if (t != Double) switch (t) {
case QCborValue::Double: {
double d;
memcpy(&d, &n, sizeof(d));
return d;
}
case QCborValue::Integer:
return n;
default:
return defaultValue; return defaultValue;
return dbl; }
} }
/*! /*!
@ -621,11 +654,7 @@ double QJsonValue::toDouble(double defaultValue) const
*/ */
QString QJsonValue::toString(const QString &defaultValue) const QString QJsonValue::toString(const QString &defaultValue) const
{ {
if (t != String) return (t == QCborValue::String && d) ? d->stringAt(n) : defaultValue;
return defaultValue;
stringData->ref.ref(); // the constructor below doesn't add a ref.
QStringDataPtr holder = { stringData };
return QString(holder);
} }
/*! /*!
@ -637,11 +666,7 @@ QString QJsonValue::toString(const QString &defaultValue) const
*/ */
QString QJsonValue::toString() const QString QJsonValue::toString() const
{ {
if (t != String) return (t == QCborValue::String && d) ? d->stringAt(n) : QString();
return QString();
stringData->ref.ref(); // the constructor below doesn't add a ref.
QStringDataPtr holder = { stringData };
return QString(holder);
} }
/*! /*!
@ -651,10 +676,10 @@ QString QJsonValue::toString() const
*/ */
QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const
{ {
if (!d || t != Array) if (t != QCborValue::Array || n >= 0 || !d)
return defaultValue; return defaultValue;
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(base)); return QJsonArray(d.data());
} }
/*! /*!
@ -676,10 +701,10 @@ QJsonArray QJsonValue::toArray() const
*/ */
QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const
{ {
if (!d || t != Object) if (t != QCborValue::Map || n >= 0 || !d)
return defaultValue; return defaultValue;
return QJsonObject(d, static_cast<QJsonPrivate::Object *>(base)); return QJsonObject(d.data());
} }
/*! /*!
@ -766,33 +791,31 @@ bool QJsonValue::operator==(const QJsonValue &other) const
return false; return false;
switch (t) { switch (t) {
case Undefined: case QCborValue::Undefined:
case Null: case QCborValue::Null:
case QCborValue::True:
case QCborValue::False:
break; break;
case Bool: case QCborValue::Double:
return b == other.b; return toDouble() == other.toDouble();
case Double: case QCborValue::Integer:
return dbl == other.dbl; return n == other.n;
case String: case QCborValue::String:
return toString() == other.toString(); return toString() == other.toString();
case Array: case QCborValue::Array:
if (base == other.base) if (!d)
return true; return !other.d || other.d->elements.length() == 0;
if (!base) if (!other.d)
return !other.base->length; return d->elements.length() == 0;
if (!other.base) return QJsonArray(d.data()) == QJsonArray(other.d.data());
return !base->length; case QCborValue::Map:
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(base)) if (!d)
== QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(other.base)); return !other.d || other.d->elements.length() == 0;
case Object: if (!other.d)
if (base == other.base) return d->elements.length() == 0;
return true; return QJsonObject(d.data()) == QJsonObject(other.d.data());
if (!base) default:
return !other.base->length; return false;
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; return true;
} }
@ -810,15 +833,7 @@ bool QJsonValue::operator!=(const QJsonValue &other) const
*/ */
void QJsonValue::detach() void QJsonValue::detach()
{ {
if (!d) d.detach();
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());
} }
@ -914,7 +929,7 @@ uint qHash(const QJsonValue &value, uint seed)
QDebug operator<<(QDebug dbg, const QJsonValue &o) QDebug operator<<(QDebug dbg, const QJsonValue &o)
{ {
QDebugStateSaver saver(dbg); QDebugStateSaver saver(dbg);
switch (o.t) { switch (o.type()) {
case QJsonValue::Undefined: case QJsonValue::Undefined:
dbg << "QJsonValue(undefined)"; dbg << "QJsonValue(undefined)";
break; break;
@ -948,7 +963,7 @@ QDebug operator<<(QDebug dbg, const QJsonValue &o)
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
QDataStream &operator<<(QDataStream &stream, const QJsonValue &v) QDataStream &operator<<(QDataStream &stream, const QJsonValue &v)
{ {
quint8 type = v.t; quint8 type = v.type();
stream << type; stream << type;
switch (type) { switch (type) {
case QJsonValue::Undefined: case QJsonValue::Undefined:

View File

@ -42,22 +42,18 @@
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include <QtCore/qstring.h> #include <QtCore/qstring.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qcborvalue.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDebug;
class QVariant; class QVariant;
class QJsonArray; class QJsonArray;
class QJsonObject; class QJsonObject;
class QCborContainerPrivate;
namespace QJsonPrivate { namespace QJsonPrivate {
class Data; class Value;
class Base;
class Object;
class Header;
class Array;
class Value;
class Entry;
} }
class Q_CORE_EXPORT QJsonValue class Q_CORE_EXPORT QJsonValue
@ -77,12 +73,12 @@ public:
QJsonValue(bool b); QJsonValue(bool b);
QJsonValue(double n); QJsonValue(double n);
QJsonValue(int n); QJsonValue(int n);
QJsonValue(qint64 n); QJsonValue(qint64 v);
QJsonValue(const QString &s); QJsonValue(const QString &s);
QJsonValue(QLatin1String s); QJsonValue(QLatin1String s);
#ifndef QT_NO_CAST_FROM_ASCII #ifndef QT_NO_CAST_FROM_ASCII
inline QT_ASCII_CAST_WARN QJsonValue(const char *s) inline QT_ASCII_CAST_WARN QJsonValue(const char *s)
: d(nullptr), t(String) { stringDataFromQStringHelper(QString::fromUtf8(s)); } : QJsonValue(QString::fromUtf8(s)) {}
#endif #endif
QJsonValue(const QJsonArray &a); QJsonValue(const QJsonArray &a);
QJsonValue(const QJsonObject &o); QJsonValue(const QJsonObject &o);
@ -92,15 +88,7 @@ public:
QJsonValue(const QJsonValue &other); QJsonValue(const QJsonValue &other);
QJsonValue &operator =(const QJsonValue &other); QJsonValue &operator =(const QJsonValue &other);
QJsonValue(QJsonValue &&other) noexcept QJsonValue(QJsonValue &&other) noexcept;
: ui(other.ui),
d(other.d),
t(other.t)
{
other.ui = 0;
other.d = nullptr;
other.t = Null;
}
QJsonValue &operator =(QJsonValue &&other) noexcept QJsonValue &operator =(QJsonValue &&other) noexcept
{ {
@ -108,12 +96,7 @@ public:
return *this; return *this;
} }
void swap(QJsonValue &other) noexcept void swap(QJsonValue &other) noexcept;
{
qSwap(ui, other.ui);
qSwap(d, other.d);
qSwap(t, other.t);
}
static QJsonValue fromVariant(const QVariant &variant); static QJsonValue fromVariant(const QVariant &variant);
QVariant toVariant() const; QVariant toVariant() const;
@ -149,7 +132,7 @@ public:
private: private:
// avoid implicit conversions from char * to bool // avoid implicit conversions from char * to bool
inline QJsonValue(const void *) {} QJsonValue(const void *) = delete;
friend class QJsonPrivate::Value; friend class QJsonPrivate::Value;
friend class QJsonArray; friend class QJsonArray;
friend class QJsonObject; friend class QJsonObject;
@ -157,20 +140,19 @@ private:
friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &);
friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonValue &); friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonValue &);
QJsonValue(QJsonPrivate::Data *d, QJsonPrivate::Base *b, const QJsonPrivate::Value& v); // ### Qt6: Remove this.
void stringDataFromQStringHelper(const QString &string); void stringDataFromQStringHelper(const QString &string);
void detach(); void detach();
union { // ### Qt6: change to an actual QCborValue
quint64 ui; qint64 n = 0;
bool b; QExplicitlySharedDataPointer<QCborContainerPrivate> d; // needed for Objects, Arrays, Strings
double dbl; QCborValue::Type t;
QStringData *stringData;
QJsonPrivate::Base *base; // Assert binary compatibility with pre-5.15 QJsonValue
}; Q_STATIC_ASSERT(sizeof(QExplicitlySharedDataPointer<QCborContainerPrivate>) == sizeof(void *));
QJsonPrivate::Data *d; // needed for Objects and Arrays Q_STATIC_ASSERT(sizeof(QCborValue::Type) == sizeof(QJsonValue::Type));
Type t;
}; };
class Q_CORE_EXPORT QJsonValueRef class Q_CORE_EXPORT QJsonValueRef

View File

@ -44,13 +44,14 @@
#include "qjson_p.h" #include "qjson_p.h"
#include "private/qutfcodec_p.h" #include "private/qutfcodec_p.h"
#include <private/qnumeric_p.h> #include <private/qnumeric_p.h>
#include <private/qcborvalue_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
using namespace QJsonPrivate; using namespace QJsonPrivate;
static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact); static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact);
static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact); static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact);
static inline uchar hexdig(uint u) static inline uchar hexdig(uint u)
{ {
@ -126,16 +127,20 @@ static QByteArray escapedString(const QString &s)
return ba; return ba;
} }
static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &v, QByteArray &json, int indent, bool compact) static void valueToJson(const QCborValue &v, QByteArray &json, int indent, bool compact)
{ {
QJsonValue::Type type = (QJsonValue::Type)(uint)v.type; QCborValue::Type type = v.type();
switch (type) { switch (type) {
case QJsonValue::Bool: case QCborValue::True:
json += v.toBoolean() ? "true" : "false"; json += "true";
break; break;
case QJsonValue::Double: { case QCborValue::False:
const double d = v.toDouble(b); json += "false";
if (qIsFinite(d)) { // +2 to format to ensure the expected precision break;
case QCborValue::Integer:
case QCborValue::Double: {
const double d = v.toDouble();
if (qIsFinite(d)) {
quint64 absInt; quint64 absInt;
json += QByteArray::number(d, convertDoubleTo(std::abs(d), &absInt) ? 'f' : 'g', json += QByteArray::number(d, convertDoubleTo(std::abs(d), &absInt) ? 'f' : 'g',
QLocale::FloatingPointShortest); QLocale::FloatingPointShortest);
@ -144,42 +149,44 @@ static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &
} }
break; break;
} }
case QJsonValue::String: case QCborValue::String:
json += '"'; json += '"';
json += escapedString(v.toString(b)); json += escapedString(v.toString());
json += '"'; json += '"';
break; break;
case QJsonValue::Array: case QCborValue::Array:
json += compact ? "[" : "[\n"; json += compact ? "[" : "[\n";
arrayContentToJson(static_cast<QJsonPrivate::Array *>(v.base(b)), json, indent + (compact ? 0 : 1), compact); arrayContentToJson(
QJsonPrivate::Value::container(v), json, indent + (compact ? 0 : 1), compact);
json += QByteArray(4*indent, ' '); json += QByteArray(4*indent, ' ');
json += ']'; json += ']';
break; break;
case QJsonValue::Object: case QCborValue::Map:
json += compact ? "{" : "{\n"; json += compact ? "{" : "{\n";
objectContentToJson(static_cast<QJsonPrivate::Object *>(v.base(b)), json, indent + (compact ? 0 : 1), compact); objectContentToJson(
QJsonPrivate::Value::container(v), json, indent + (compact ? 0 : 1), compact);
json += QByteArray(4*indent, ' '); json += QByteArray(4*indent, ' ');
json += '}'; json += '}';
break; break;
case QJsonValue::Null: case QCborValue::Null:
default: default:
json += "null"; json += "null";
} }
} }
static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact)
{ {
if (!a || !a->length) if (!a || a->elements.empty())
return; return;
QByteArray indentString(4*indent, ' '); QByteArray indentString(4*indent, ' ');
uint i = 0; qsizetype i = 0;
while (1) { while (true) {
json += indentString; json += indentString;
valueToJson(a, a->at(i), json, indent, compact); valueToJson(a->valueAt(i), json, indent, compact);
if (++i == a->length) { if (++i == a->elements.size()) {
if (!compact) if (!compact)
json += '\n'; json += '\n';
break; break;
@ -190,23 +197,23 @@ static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, i
} }
static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact)
{ {
if (!o || !o->length) if (!o || o->elements.empty())
return; return;
QByteArray indentString(4*indent, ' '); QByteArray indentString(4*indent, ' ');
uint i = 0; qsizetype i = 0;
while (1) { while (true) {
QJsonPrivate::Entry *e = o->entryAt(i); QCborValue e = o->valueAt(i);
json += indentString; json += indentString;
json += '"'; json += '"';
json += escapedString(e->key()); json += escapedString(o->valueAt(i).toString());
json += compact ? "\":" : "\": "; json += compact ? "\":" : "\": ";
valueToJson(o, e->value, json, indent, compact); valueToJson(o->valueAt(i + 1), json, indent, compact);
if (++i == o->length) { if ((i += 2) == o->elements.size()) {
if (!compact) if (!compact)
json += '\n'; json += '\n';
break; break;
@ -216,18 +223,18 @@ static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json,
} }
} }
void Writer::objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) void Writer::objectToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact)
{ {
json.reserve(json.size() + (o ? (int)o->size : 16)); json.reserve(json.size() + (o ? (int)o->elements.size() : 16));
json += compact ? "{" : "{\n"; json += compact ? "{" : "{\n";
objectContentToJson(o, json, indent + (compact ? 0 : 1), compact); objectContentToJson(o, json, indent + (compact ? 0 : 1), compact);
json += QByteArray(4*indent, ' '); json += QByteArray(4*indent, ' ');
json += compact ? "}" : "}\n"; json += compact ? "}" : "}\n";
} }
void Writer::arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) void Writer::arrayToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact)
{ {
json.reserve(json.size() + (a ? (int)a->size : 16)); json.reserve(json.size() + (a ? (int)a->elements.size() : 16));
json += compact ? "[" : "[\n"; json += compact ? "[" : "[\n";
arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact); arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact);
json += QByteArray(4*indent, ' '); json += QByteArray(4*indent, ' ');

View File

@ -62,8 +62,8 @@ namespace QJsonPrivate
class Writer class Writer
{ {
public: public:
static void objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact = false); static void objectToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact = false);
static void arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact = false); static void arrayToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact = false);
}; };
} }

View File

@ -25,7 +25,6 @@ SOURCES += \
serialization/qcbordiagnostic.cpp \ serialization/qcbordiagnostic.cpp \
serialization/qcborvalue.cpp \ serialization/qcborvalue.cpp \
serialization/qdatastream.cpp \ serialization/qdatastream.cpp \
serialization/qjson.cpp \
serialization/qjsoncbor.cpp \ serialization/qjsoncbor.cpp \
serialization/qjsondocument.cpp \ serialization/qjsondocument.cpp \
serialization/qjsonobject.cpp \ serialization/qjsonobject.cpp \
@ -45,6 +44,20 @@ qtConfig(cborstream): {
serialization/qcborstream.h serialization/qcborstream.h
} }
qtConfig(binaryjson): {
HEADERS += \
serialization/qbinaryjson_p.h \
serialization/qbinaryjsonarray_p.h \
serialization/qbinaryjsonobject_p.h \
serialization/qbinaryjsonvalue_p.h
SOURCES += \
serialization/qbinaryjson.cpp \
serialization/qbinaryjsonarray.cpp \
serialization/qbinaryjsonobject.cpp \
serialization/qbinaryjsonvalue.cpp \
}
false: SOURCES += \ false: SOURCES += \
serialization/qcborarray.cpp \ serialization/qcborarray.cpp \
serialization/qcbormap.cpp serialization/qcbormap.cpp

View File

@ -1498,7 +1498,7 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta
case QStateMachine::StateMachineChildModeSetToParallelError: case QStateMachine::StateMachineChildModeSetToParallelError:
Q_ASSERT(currentContext != nullptr); Q_ASSERT(currentContext != nullptr);
errorString = QStateMachine::tr("Child mode of state machine '%1' is not 'ExclusiveStates'!") errorString = QStateMachine::tr("Child mode of state machine '%1' is not 'ExclusiveStates'.")
.arg(currentContext->objectName()); .arg(currentContext->objectName());
break; break;
@ -2469,7 +2469,7 @@ QStateMachine::QStateMachine(QObject *parent)
and \a parent. and \a parent.
\warning Do not set the \a childMode to anything else than \l{ExclusiveStates}, otherwise the \warning Do not set the \a childMode to anything else than \l{ExclusiveStates}, otherwise the
state machine is invalid, and might work incorrectly! state machine is invalid, and might work incorrectly.
*/ */
QStateMachine::QStateMachine(QState::ChildMode childMode, QObject *parent) QStateMachine::QStateMachine(QState::ChildMode childMode, QObject *parent)
: QState(*new QStateMachinePrivate, /*parentState=*/0) : QState(*new QStateMachinePrivate, /*parentState=*/0)

View File

@ -71,21 +71,6 @@ QT_END_NAMESPACE
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// Macro QSHAREDPOINTER_VERIFY_AUTO_CAST
// generates a compiler error if the following construct isn't valid:
// T *ptr1;
// X *ptr2 = ptr1;
//
#ifdef QT_NO_DEBUG
# define QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X) qt_noop()
#else
template<typename T> inline void qt_sharedpointer_cast_check(T *) { }
# define QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X) \
qt_sharedpointer_cast_check<T>(static_cast<X *>(0))
#endif
// //
// forward declarations // forward declarations
// //
@ -299,6 +284,9 @@ template <class T> class QSharedPointer
{ {
typedef T *QSharedPointer:: *RestrictedBool; typedef T *QSharedPointer:: *RestrictedBool;
typedef QtSharedPointer::ExternalRefCountData Data; typedef QtSharedPointer::ExternalRefCountData Data;
template <typename X>
using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;
public: public:
typedef T Type; typedef T Type;
typedef T element_type; typedef T element_type;
@ -322,11 +310,11 @@ public:
Q_DECL_CONSTEXPR QSharedPointer(std::nullptr_t) noexcept : value(nullptr), d(nullptr) { } Q_DECL_CONSTEXPR QSharedPointer(std::nullptr_t) noexcept : value(nullptr), d(nullptr) { }
template <class X> template <class X, IfCompatible<X> = true>
inline explicit QSharedPointer(X *ptr) : value(ptr) // noexcept inline explicit QSharedPointer(X *ptr) : value(ptr) // noexcept
{ internalConstruct(ptr, QtSharedPointer::NormalDeleter()); } { internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }
template <class X, typename Deleter> template <class X, typename Deleter, IfCompatible<X> = true>
inline QSharedPointer(X *ptr, Deleter deleter) : value(ptr) // throws inline QSharedPointer(X *ptr, Deleter deleter) : value(ptr) // throws
{ internalConstruct(ptr, deleter); } { internalConstruct(ptr, deleter); }
@ -354,7 +342,7 @@ public:
return *this; return *this;
} }
template <class X> template <class X, IfCompatible<X> = true>
QSharedPointer(QSharedPointer<X> &&other) noexcept QSharedPointer(QSharedPointer<X> &&other) noexcept
: value(other.value), d(other.d) : value(other.value), d(other.d)
{ {
@ -362,7 +350,7 @@ public:
other.value = nullptr; other.value = nullptr;
} }
template <class X> template <class X, IfCompatible<X> = true>
QSharedPointer &operator=(QSharedPointer<X> &&other) noexcept QSharedPointer &operator=(QSharedPointer<X> &&other) noexcept
{ {
QSharedPointer moved(std::move(other)); QSharedPointer moved(std::move(other));
@ -370,11 +358,11 @@ public:
return *this; return *this;
} }
template <class X> template <class X, IfCompatible<X> = true>
QSharedPointer(const QSharedPointer<X> &other) noexcept : value(other.value), d(other.d) QSharedPointer(const QSharedPointer<X> &other) noexcept : value(other.value), d(other.d)
{ if (d) ref(); } { if (d) ref(); }
template <class X> template <class X, IfCompatible<X> = true>
inline QSharedPointer &operator=(const QSharedPointer<X> &other) inline QSharedPointer &operator=(const QSharedPointer<X> &other)
{ {
QSharedPointer copy(other); QSharedPointer copy(other);
@ -382,11 +370,11 @@ public:
return *this; return *this;
} }
template <class X> template <class X, IfCompatible<X> = true>
inline QSharedPointer(const QWeakPointer<X> &other) : value(nullptr), d(nullptr) inline QSharedPointer(const QWeakPointer<X> &other) : value(nullptr), d(nullptr)
{ *this = other; } { *this = other; }
template <class X> template <class X, IfCompatible<X> = true>
inline QSharedPointer<T> &operator=(const QWeakPointer<X> &other) inline QSharedPointer<T> &operator=(const QWeakPointer<X> &other)
{ internalSet(other.d, other.value); return *this; } { internalSet(other.d, other.value); return *this; }
@ -556,6 +544,8 @@ class QWeakPointer
{ {
typedef T *QWeakPointer:: *RestrictedBool; typedef T *QWeakPointer:: *RestrictedBool;
typedef QtSharedPointer::ExternalRefCountData Data; typedef QtSharedPointer::ExternalRefCountData Data;
template <typename X>
using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;
public: public:
typedef T element_type; typedef T element_type;
@ -581,14 +571,14 @@ public:
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT
// special constructor that is enabled only if X derives from QObject // special constructor that is enabled only if X derives from QObject
#if QT_DEPRECATED_SINCE(5, 0) #if QT_DEPRECATED_SINCE(5, 0)
template <class X> template <class X, IfCompatible<X> = true>
QT_DEPRECATED inline QWeakPointer(X *ptr) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr) QT_DEPRECATED inline QWeakPointer(X *ptr) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
{ } { }
#endif #endif
#endif #endif
#if QT_DEPRECATED_SINCE(5, 0) #if QT_DEPRECATED_SINCE(5, 0)
template <class X> template <class X, IfCompatible<X> = true>
QT_DEPRECATED inline QWeakPointer &operator=(X *ptr) QT_DEPRECATED inline QWeakPointer &operator=(X *ptr)
{ return *this = QWeakPointer(ptr); } { return *this = QWeakPointer(ptr); }
#endif #endif
@ -624,11 +614,11 @@ public:
return *this; return *this;
} }
template <class X> template <class X, IfCompatible<X> = true>
inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr) inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr)
{ *this = o; } { *this = o; }
template <class X> template <class X, IfCompatible<X> = true>
inline QWeakPointer &operator=(const QWeakPointer<X> &o) inline QWeakPointer &operator=(const QWeakPointer<X> &o)
{ {
// conversion between X and T could require access to the virtual table // conversion between X and T could require access to the virtual table
@ -645,14 +635,13 @@ public:
bool operator!=(const QWeakPointer<X> &o) const noexcept bool operator!=(const QWeakPointer<X> &o) const noexcept
{ return !(*this == o); } { return !(*this == o); }
template <class X> template <class X, IfCompatible<X> = true>
inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr) inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr)
{ *this = o; } { *this = o; }
template <class X> template <class X, IfCompatible<X> = true>
inline QWeakPointer &operator=(const QSharedPointer<X> &o) inline QWeakPointer &operator=(const QSharedPointer<X> &o)
{ {
QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X); // if you get an error in this line, the cast is invalid
internalSet(o.d, o.data()); internalSet(o.d, o.data());
return *this; return *this;
} }
@ -689,7 +678,7 @@ public:
{ return *this = QWeakPointer<X>(ptr, true); } { return *this = QWeakPointer<X>(ptr, true); }
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT
template <class X> template <class X, IfCompatible<X> = true>
inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr) inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
{ } { }
#endif #endif

View File

@ -4145,11 +4145,11 @@ QPaintEngine *QImage::paintEngine() const
if (!d->paintEngine) { if (!d->paintEngine) {
QPaintDevice *paintDevice = const_cast<QImage *>(this); QPaintDevice *paintDevice = const_cast<QImage *>(this);
QPaintEngine *paintEngine = 0;
QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
if (platformIntegration) if (platformIntegration)
paintEngine = platformIntegration->createImagePaintEngine(paintDevice); d->paintEngine = platformIntegration->createImagePaintEngine(paintDevice);
d->paintEngine = paintEngine ? paintEngine : new QRasterPaintEngine(paintDevice); if (!d->paintEngine)
d->paintEngine = new QRasterPaintEngine(paintDevice);
} }
return d->paintEngine; return d->paintEngine;

View File

@ -1869,7 +1869,20 @@ bool QGuiApplication::event(QEvent *e)
{ {
if(e->type() == QEvent::LanguageChange) { if(e->type() == QEvent::LanguageChange) {
setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight); setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight);
} else if (e->type() == QEvent::Quit) {
// Close open windows. This is done in order to deliver de-expose
// events while the event loop is still running.
for (QWindow *topLevelWindow : QGuiApplication::topLevelWindows()) {
// Already closed windows will not have a platform window, skip those
if (!topLevelWindow->handle())
continue;
if (!topLevelWindow->close()) {
e->ignore();
return true;
}
}
} }
return QCoreApplication::event(e); return QCoreApplication::event(e);
} }
@ -1946,6 +1959,9 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e); QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e);
QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); } QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); }
break; break;
case QWindowSystemInterfacePrivate::ApplicationTermination:
QGuiApplicationPrivate::processApplicationTermination(e);
break;
case QWindowSystemInterfacePrivate::FlushEvents: { case QWindowSystemInterfacePrivate::FlushEvents: {
QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast<QWindowSystemInterfacePrivate::FlushEventsEvent *>(e); QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast<QWindowSystemInterfacePrivate::FlushEventsEvent *>(e);
QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); } QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); }
@ -3495,6 +3511,13 @@ bool QGuiApplicationPrivate::tryCloseRemainingWindows(QWindowList processedWindo
return true; return true;
} }
void QGuiApplicationPrivate::processApplicationTermination(QWindowSystemInterfacePrivate::WindowSystemEvent *windowSystemEvent)
{
QEvent event(QEvent::Quit);
QGuiApplication::sendSpontaneousEvent(QGuiApplication::instance(), &event);
windowSystemEvent->eventAccepted = event.isAccepted();
}
/*! /*!
\since 5.2 \since 5.2
\fn Qt::ApplicationState QGuiApplication::applicationState() \fn Qt::ApplicationState QGuiApplication::applicationState()

View File

@ -144,6 +144,8 @@ public:
static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e); static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e);
static void processApplicationTermination(QWindowSystemInterfacePrivate::WindowSystemEvent *e);
static void updateFilteredScreenOrientation(QScreen *screen); static void updateFilteredScreenOrientation(QScreen *screen);
static void reportScreenOrientationChange(QScreen *screen); static void reportScreenOrientationChange(QScreen *screen);
static void processScreenOrientationChange(QWindowSystemInterfacePrivate::ScreenOrientationEvent *e); static void processScreenOrientationChange(QWindowSystemInterfacePrivate::ScreenOrientationEvent *e);

View File

@ -490,6 +490,8 @@ static const struct {
{ Qt::Key_LaunchD, QT_TRANSLATE_NOOP("QShortcut", "Launch (D)") }, { Qt::Key_LaunchD, QT_TRANSLATE_NOOP("QShortcut", "Launch (D)") },
{ Qt::Key_LaunchE, QT_TRANSLATE_NOOP("QShortcut", "Launch (E)") }, { Qt::Key_LaunchE, QT_TRANSLATE_NOOP("QShortcut", "Launch (E)") },
{ Qt::Key_LaunchF, QT_TRANSLATE_NOOP("QShortcut", "Launch (F)") }, { Qt::Key_LaunchF, QT_TRANSLATE_NOOP("QShortcut", "Launch (F)") },
{ Qt::Key_LaunchG, QT_TRANSLATE_NOOP("QShortcut", "Launch (G)") },
{ Qt::Key_LaunchH, QT_TRANSLATE_NOOP("QShortcut", "Launch (H)") },
{ Qt::Key_MonBrightnessUp, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Up") }, { Qt::Key_MonBrightnessUp, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Up") },
{ Qt::Key_MonBrightnessDown, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Down") }, { Qt::Key_MonBrightnessDown, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Down") },
{ Qt::Key_KeyboardLightOnOff, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Light On/Off") }, { Qt::Key_KeyboardLightOnOff, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Light On/Off") },
@ -516,9 +518,11 @@ static const struct {
{ Qt::Key_Book, QT_TRANSLATE_NOOP("QShortcut", "Book") }, { Qt::Key_Book, QT_TRANSLATE_NOOP("QShortcut", "Book") },
{ Qt::Key_CD, QT_TRANSLATE_NOOP("QShortcut", "CD") }, { Qt::Key_CD, QT_TRANSLATE_NOOP("QShortcut", "CD") },
{ Qt::Key_Calculator, QT_TRANSLATE_NOOP("QShortcut", "Calculator") }, { Qt::Key_Calculator, QT_TRANSLATE_NOOP("QShortcut", "Calculator") },
{ Qt::Key_Calendar, QT_TRANSLATE_NOOP("QShortcut", "Calendar") },
{ Qt::Key_Clear, QT_TRANSLATE_NOOP("QShortcut", "Clear") }, { Qt::Key_Clear, QT_TRANSLATE_NOOP("QShortcut", "Clear") },
{ Qt::Key_ClearGrab, QT_TRANSLATE_NOOP("QShortcut", "Clear Grab") }, { Qt::Key_ClearGrab, QT_TRANSLATE_NOOP("QShortcut", "Clear Grab") },
{ Qt::Key_Close, QT_TRANSLATE_NOOP("QShortcut", "Close") }, { Qt::Key_Close, QT_TRANSLATE_NOOP("QShortcut", "Close") },
{ Qt::Key_ContrastAdjust, QT_TRANSLATE_NOOP("QShortcut", "Adjust contrast") },
{ Qt::Key_Copy, QT_TRANSLATE_NOOP("QShortcut", "Copy") }, { Qt::Key_Copy, QT_TRANSLATE_NOOP("QShortcut", "Copy") },
{ Qt::Key_Cut, QT_TRANSLATE_NOOP("QShortcut", "Cut") }, { Qt::Key_Cut, QT_TRANSLATE_NOOP("QShortcut", "Cut") },
{ Qt::Key_Display, QT_TRANSLATE_NOOP("QShortcut", "Display") }, { Qt::Key_Display, QT_TRANSLATE_NOOP("QShortcut", "Display") },
@ -532,6 +536,7 @@ static const struct {
{ Qt::Key_LogOff, QT_TRANSLATE_NOOP("QShortcut", "Logoff") }, { Qt::Key_LogOff, QT_TRANSLATE_NOOP("QShortcut", "Logoff") },
{ Qt::Key_Market, QT_TRANSLATE_NOOP("QShortcut", "Market") }, { Qt::Key_Market, QT_TRANSLATE_NOOP("QShortcut", "Market") },
{ Qt::Key_Meeting, QT_TRANSLATE_NOOP("QShortcut", "Meeting") }, { Qt::Key_Meeting, QT_TRANSLATE_NOOP("QShortcut", "Meeting") },
{ Qt::Key_Memo, QT_TRANSLATE_NOOP("QShortcut", "Memo") },
{ Qt::Key_MenuKB, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Menu") }, { Qt::Key_MenuKB, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Menu") },
{ Qt::Key_MenuPB, QT_TRANSLATE_NOOP("QShortcut", "Menu PB") }, { Qt::Key_MenuPB, QT_TRANSLATE_NOOP("QShortcut", "Menu PB") },
{ Qt::Key_MySites, QT_TRANSLATE_NOOP("QShortcut", "My Sites") }, { Qt::Key_MySites, QT_TRANSLATE_NOOP("QShortcut", "My Sites") },
@ -552,6 +557,7 @@ static const struct {
{ Qt::Key_Support, QT_TRANSLATE_NOOP("QShortcut", "Support") }, { Qt::Key_Support, QT_TRANSLATE_NOOP("QShortcut", "Support") },
{ Qt::Key_TaskPane, QT_TRANSLATE_NOOP("QShortcut", "Task Panel") }, { Qt::Key_TaskPane, QT_TRANSLATE_NOOP("QShortcut", "Task Panel") },
{ Qt::Key_Terminal, QT_TRANSLATE_NOOP("QShortcut", "Terminal") }, { Qt::Key_Terminal, QT_TRANSLATE_NOOP("QShortcut", "Terminal") },
{ Qt::Key_ToDoList, QT_TRANSLATE_NOOP("QShortcut", "To-do list") },
{ Qt::Key_Tools, QT_TRANSLATE_NOOP("QShortcut", "Tools") }, { Qt::Key_Tools, QT_TRANSLATE_NOOP("QShortcut", "Tools") },
{ Qt::Key_Travel, QT_TRANSLATE_NOOP("QShortcut", "Travel") }, { Qt::Key_Travel, QT_TRANSLATE_NOOP("QShortcut", "Travel") },
{ Qt::Key_Video, QT_TRANSLATE_NOOP("QShortcut", "Video") }, { Qt::Key_Video, QT_TRANSLATE_NOOP("QShortcut", "Video") },

View File

@ -285,6 +285,12 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleApplicationStateChanged, Qt::Application
QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
} }
QT_DEFINE_QPA_EVENT_HANDLER(bool, handleApplicationTermination)
{
auto *e = new QWindowSystemInterfacePrivate::WindowSystemEvent(QWindowSystemInterfacePrivate::ApplicationTermination);
return QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
}
QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry) QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry)
: WindowSystemEvent(GeometryChange) : WindowSystemEvent(GeometryChange)
, window(window) , window(window)

View File

@ -215,6 +215,9 @@ public:
template<typename Delivery = QWindowSystemInterface::DefaultDelivery> template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleApplicationStateChanged(Qt::ApplicationState newState, bool forcePropagate = false); static void handleApplicationStateChanged(Qt::ApplicationState newState, bool forcePropagate = false);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static bool handleApplicationTermination();
#if QT_CONFIG(draganddrop) #if QT_CONFIG(draganddrop)
#if QT_DEPRECATED_SINCE(5, 11) #if QT_DEPRECATED_SINCE(5, 11)
QT_DEPRECATED static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData, QT_DEPRECATED static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData,

View File

@ -99,7 +99,8 @@ public:
ApplicationStateChanged = 0x19, ApplicationStateChanged = 0x19,
FlushEvents = 0x20, FlushEvents = 0x20,
WindowScreenChanged = 0x21, WindowScreenChanged = 0x21,
SafeAreaMarginsChanged = 0x22 SafeAreaMarginsChanged = 0x22,
ApplicationTermination = 0x23
}; };
class WindowSystemEvent { class WindowSystemEvent {

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_2_Compatibility : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_2_Compatibility : public QAbstractOpenGLFunctions
@ -5632,6 +5638,10 @@ inline void QOpenGLFunctions_4_2_Compatibility::glVertexAttribI1i(GLuint index,
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_2_Core : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_2_Core : public QAbstractOpenGLFunctions
@ -3027,6 +3033,10 @@ inline void QOpenGLFunctions_4_2_Core::glDrawArraysInstancedBaseInstance(GLenum
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_3_Compatibility : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_3_Compatibility : public QAbstractOpenGLFunctions
@ -5839,6 +5845,10 @@ inline void QOpenGLFunctions_4_3_Compatibility::glVertexAttribI1i(GLuint index,
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -57,6 +57,13 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_3_Core : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_3_Core : public QAbstractOpenGLFunctions
@ -3230,6 +3237,10 @@ inline void QOpenGLFunctions_4_3_Core::glClearBufferData(GLenum target, GLenum i
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -59,6 +59,12 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
class Q_GUI_EXPORT QOpenGLFunctions_4_4_Compatibility : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_4_Compatibility : public QAbstractOpenGLFunctions
{ {
public: public:
@ -5961,6 +5967,10 @@ inline void QOpenGLFunctions_4_4_Compatibility::glVertexP2ui(GLenum type, GLuint
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_4_Core : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_4_Core : public QAbstractOpenGLFunctions
@ -3415,6 +3421,10 @@ inline void QOpenGLFunctions_4_4_Core::glBufferStorage(GLenum target, GLsizeiptr
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_5_Compatibility : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_5_Compatibility : public QAbstractOpenGLFunctions
@ -6679,6 +6685,10 @@ inline void QOpenGLFunctions_4_5_Compatibility::glGetnMapdv(GLenum target, GLenu
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions> #include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_5_Core : public QAbstractOpenGLFunctions class Q_GUI_EXPORT QOpenGLFunctions_4_5_Core : public QAbstractOpenGLFunctions
@ -4056,6 +4062,11 @@ inline void QOpenGLFunctions_4_5_Core::glClipControl(GLenum origin, GLenum depth
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2 #endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif #endif

View File

@ -61,6 +61,12 @@
#include <QtCore/qpair.h> #include <QtCore/qpair.h>
#include <QtGui/qopengl.h> #include <QtGui/qopengl.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QOpenGLContext; class QOpenGLContext;
@ -1897,6 +1903,10 @@ public:
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL #endif // QT_NO_OPENGL
#endif #endif

View File

@ -3518,14 +3518,36 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
\c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be \c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be
resized. resized.
\note Typical rendering logic will call this function to get the output
size when starting to prepare a new frame, and base dependent calculations
(such as, the viewport) on the size returned from this function.
While in many cases the value is the same as \c{QWindow::size() *
QWindow::devicePixelRatio()}, relying on the QWindow-reported size is not
guaranteed to be correct on all platforms and graphics API implementations.
Using this function is therefore strongly recommended whenever there is a
need to identify the dimensions, in pixels, of the output layer or surface.
This also has the added benefit of avoiding potential data races when QRhi
is used on a dedicated rendering thread, because the need to call QWindow
functions, that may then access data updated on the main thread, is
avoided.
\sa surfacePixelSize() \sa surfacePixelSize()
*/ */
/*! /*!
\fn QSize QRhiSwapChain::surfacePixelSize() \fn QSize QRhiSwapChain::surfacePixelSize()
\return The size of the window's associated surface or layer. Do not assume \return The size of the window's associated surface or layer.
this is the same as QWindow::size() * QWindow::devicePixelRatio().
\warning Do not assume this is the same as \c{QWindow::size() *
QWindow::devicePixelRatio()}. With some graphics APIs and windowing system
interfaces (for example, Vulkan) there is a theoretical possibility for a
surface to assume a size different from the associated window. To support
these cases, rendering logic must always base size-derived calculations
(such as, viewports) on the size reported from QRhiSwapChain, and never on
the size queried from QWindow.
\note Can also be called before buildOrResize(), if at least window() is \note Can also be called before buildOrResize(), if at least window() is
already set) This in combination with currentPixelSize() allows to detect already set) This in combination with currentPixelSize() allows to detect

View File

@ -2961,7 +2961,7 @@ bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
return checker.get(ctx)->isSupported(); return checker.get(ctx)->isSupported();
} }
static QOpenGLProgramBinaryCache qrhi_programBinaryCache; Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type) static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
{ {
@ -2995,7 +2995,7 @@ QRhiGles2::DiskCacheResult QRhiGles2::tryLoadFromDiskCache(const QRhiShaderStage
} }
diskCacheKey = binaryProgram.cacheKey(); diskCacheKey = binaryProgram.cacheKey();
if (qrhi_programBinaryCache.load(diskCacheKey, program)) { if (qrhi_programBinaryCache()->load(diskCacheKey, program)) {
qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s", qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
program, diskCacheKey.constData()); program, diskCacheKey.constData());
result = QRhiGles2::DiskCacheHit; result = QRhiGles2::DiskCacheHit;
@ -3013,7 +3013,7 @@ void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
if (isProgramBinaryDiskCacheEnabled()) { if (isProgramBinaryDiskCacheEnabled()) {
qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s", qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
program, cacheKey.constData()); program, cacheKey.constData());
qrhi_programBinaryCache.save(cacheKey, program); qrhi_programBinaryCache()->save(cacheKey, program);
} }
} }

View File

@ -35,8 +35,6 @@
****************************************************************************/ ****************************************************************************/
#include "qrhimetal_p_p.h" #include "qrhimetal_p_p.h"
#include "qshader_p.h"
#include "qshaderdescription_p.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QWindow> #include <QWindow>
#include <qmath.h> #include <qmath.h>
@ -143,8 +141,10 @@ struct QMetalShader
id<MTLLibrary> lib = nil; id<MTLLibrary> lib = nil;
id<MTLFunction> func = nil; id<MTLFunction> func = nil;
std::array<uint, 3> localSize; std::array<uint, 3> localSize;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
void release() { void release() {
nativeResourceBindingMap.clear();
[lib release]; [lib release];
lib = nil; lib = nil;
[func release]; [func release];
@ -164,7 +164,7 @@ struct QRhiMetalData
const QRhiDepthStencilClearValue &depthStencilClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue,
int colorAttCount); int colorAttCount);
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant, id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint); QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint); id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
struct DeferredReleaseEntry { struct DeferredReleaseEntry {
@ -653,18 +653,40 @@ QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings()
return new QMetalShaderResourceBindings(this); return new QMetalShaderResourceBindings(this);
} }
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, enum class BindingType {
Buffer,
Texture,
Sampler
};
static inline int mapBinding(int binding,
int stageIndex,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
BindingType type)
{
const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
if (map) {
auto it = map->constFind(binding);
if (it != map->cend())
return type == BindingType::Sampler ? it->second : it->first;
}
return binding;
}
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
QMetalCommandBuffer *cbD,
int dynamicOffsetCount, int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
bool offsetOnlyChange) bool offsetOnlyChange,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
{ {
static const int KNOWN_STAGES = 3;
struct { struct {
QRhiBatchedBindings<id<MTLBuffer> > buffers; QRhiBatchedBindings<id<MTLBuffer> > buffers;
QRhiBatchedBindings<NSUInteger> bufferOffsets; QRhiBatchedBindings<NSUInteger> bufferOffsets;
QRhiBatchedBindings<id<MTLTexture> > textures; QRhiBatchedBindings<id<MTLTexture> > textures;
QRhiBatchedBindings<id<MTLSamplerState> > samplers; QRhiBatchedBindings<id<MTLSamplerState> > samplers;
} res[KNOWN_STAGES]; } res[SUPPORTED_STAGES];
enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
const QRhiShaderResourceBinding::Data *b = binding.data(); const QRhiShaderResourceBinding::Data *b = binding.data();
@ -682,16 +704,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
} }
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
res[0].buffers.feed(b->binding, mtlbuf); res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[0].bufferOffsets.feed(b->binding, offset); res[VERTEX].bufferOffsets.feed(b->binding, offset);
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
res[1].buffers.feed(b->binding, mtlbuf); res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[1].bufferOffsets.feed(b->binding, offset); res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
res[2].buffers.feed(b->binding, mtlbuf); res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[2].bufferOffsets.feed(b->binding, offset); res[COMPUTE].bufferOffsets.feed(b->binding, offset);
} }
} }
break; break;
@ -700,16 +722,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
res[0].textures.feed(b->binding, texD->d->tex); res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
res[0].samplers.feed(b->binding, samplerD->d->samplerState); res[VERTEX].samplers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
res[1].textures.feed(b->binding, texD->d->tex); res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
res[1].samplers.feed(b->binding, samplerD->d->samplerState); res[FRAGMENT].samplers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
res[2].textures.feed(b->binding, texD->d->tex); res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
res[2].samplers.feed(b->binding, samplerD->d->samplerState); res[COMPUTE].samplers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
} }
} }
break; break;
@ -722,11 +744,11 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex); QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level); id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage))
res[0].textures.feed(b->binding, t); res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), t);
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
res[1].textures.feed(b->binding, t); res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), t);
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
res[2].textures.feed(b->binding, t); res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), t);
} }
break; break;
case QRhiShaderResourceBinding::BufferLoad: case QRhiShaderResourceBinding::BufferLoad:
@ -739,16 +761,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
id<MTLBuffer> mtlbuf = bufD->d->buf[0]; id<MTLBuffer> mtlbuf = bufD->d->buf[0];
uint offset = uint(b->u.sbuf.offset); uint offset = uint(b->u.sbuf.offset);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
res[0].buffers.feed(b->binding, mtlbuf); res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[0].bufferOffsets.feed(b->binding, offset); res[VERTEX].bufferOffsets.feed(b->binding, offset);
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
res[1].buffers.feed(b->binding, mtlbuf); res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[1].bufferOffsets.feed(b->binding, offset); res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
res[2].buffers.feed(b->binding, mtlbuf); res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[2].bufferOffsets.feed(b->binding, offset); res[COMPUTE].bufferOffsets.feed(b->binding, offset);
} }
} }
break; break;
@ -758,25 +780,30 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
} }
} }
for (int idx = 0; idx < KNOWN_STAGES; ++idx) { for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
res[idx].buffers.finish(); if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == VERTEX || stage == FRAGMENT))
res[idx].bufferOffsets.finish(); continue;
if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
continue;
for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) { res[stage].buffers.finish();
const auto &bufferBatch(res[idx].buffers.batches[i]); res[stage].bufferOffsets.finish();
const auto &offsetBatch(res[idx].bufferOffsets.batches[i]);
switch (idx) { for (int i = 0, ie = res[stage].buffers.batches.count(); i != ie; ++i) {
case 0: const auto &bufferBatch(res[stage].buffers.batches[i]);
const auto &offsetBatch(res[stage].bufferOffsets.batches[i]);
switch (stage) {
case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData() [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData() offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))]; withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break; break;
case 1: case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData() [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData() offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))]; withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break; break;
case 2: case COMPUTE:
[cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData() [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData() offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))]; withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
@ -790,21 +817,21 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
if (offsetOnlyChange) if (offsetOnlyChange)
continue; continue;
res[idx].textures.finish(); res[stage].textures.finish();
res[idx].samplers.finish(); res[stage].samplers.finish();
for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) { for (int i = 0, ie = res[stage].textures.batches.count(); i != ie; ++i) {
const auto &batch(res[idx].textures.batches[i]); const auto &batch(res[stage].textures.batches[i]);
switch (idx) { switch (stage) {
case 0: case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData() [cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case 1: case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData() [cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case 2: case COMPUTE:
[cbD->d->currentComputePassEncoder setTextures: batch.resources.constData() [cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
@ -813,18 +840,18 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break; break;
} }
} }
for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) { for (int i = 0, ie = res[stage].samplers.batches.count(); i != ie; ++i) {
const auto &batch(res[idx].samplers.batches[i]); const auto &batch(res[stage].samplers.batches[i]);
switch (idx) { switch (stage) {
case 0: case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData() [cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case 1: case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData() [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case 2: case COMPUTE:
[cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData() [cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
@ -973,18 +1000,22 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
// dynamic uniform buffer offsets always trigger a rebind // dynamic uniform buffer offsets always trigger a rebind
if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) { if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr };
if (gfxPsD) { if (gfxPsD) {
cbD->currentGraphicsSrb = srb; cbD->currentGraphicsSrb = srb;
cbD->currentComputeSrb = nullptr; cbD->currentComputeSrb = nullptr;
resBindMaps[0] = &gfxPsD->d->vs.nativeResourceBindingMap;
resBindMaps[1] = &gfxPsD->d->fs.nativeResourceBindingMap;
} else { } else {
cbD->currentGraphicsSrb = nullptr; cbD->currentGraphicsSrb = nullptr;
cbD->currentComputeSrb = srb; cbD->currentComputeSrb = srb;
resBindMaps[2] = &compPsD->d->cs.nativeResourceBindingMap;
} }
cbD->currentSrbGeneration = srbD->generation; cbD->currentSrbGeneration = srbD->generation;
cbD->currentResSlot = resSlot; cbD->currentResSlot = resSlot;
const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt; const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange); enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
} }
} }
@ -3081,9 +3112,10 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
} }
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant, id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint) QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
{ {
QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant }); QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant };
QShaderCode mtllib = shader.shader(key);
if (!mtllib.shader().isEmpty()) { if (!mtllib.shader().isEmpty()) {
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(), dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
size_t(mtllib.shader().size()), size_t(mtllib.shader().size()),
@ -3094,6 +3126,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
dispatch_release(data); dispatch_release(data);
if (!err) { if (!err) {
*entryPoint = mtllib.entryPoint(); *entryPoint = mtllib.entryPoint();
*activeKey = key;
return lib; return lib;
} else { } else {
const QString msg = QString::fromNSString(err.localizedDescription); const QString msg = QString::fromNSString(err.localizedDescription);
@ -3101,7 +3134,8 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
} }
} }
QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant }); key = { QShader::MslShader, 12, shaderVariant };
QShaderCode mslSource = shader.shader(key);
if (mslSource.shader().isEmpty()) { if (mslSource.shader().isEmpty()) {
qWarning() << "No MSL 1.2 code found in baked shader" << shader; qWarning() << "No MSL 1.2 code found in baked shader" << shader;
return nil; return nil;
@ -3122,6 +3156,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
} }
*entryPoint = mslSource.entryPoint(); *entryPoint = mslSource.entryPoint();
*activeKey = key;
return lib; return lib;
} }
@ -3195,9 +3230,12 @@ bool QMetalGraphicsPipeline::build()
break; break;
} }
} else { } else {
const QShader shader = shaderStage.shader();
QString error; QString error;
QByteArray entryPoint; QByteArray entryPoint;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint); QShaderKey activeKey;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
&error, &entryPoint, &activeKey);
if (!lib) { if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error)); qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false; return false;
@ -3218,6 +3256,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Vertex: case QRhiShaderStage::Vertex:
d->vs.lib = lib; d->vs.lib = lib;
d->vs.func = func; d->vs.func = func;
if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
d->vs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->vs); rhiD->d->shaderCache.insert(shaderStage, d->vs);
[d->vs.lib retain]; [d->vs.lib retain];
[d->vs.func retain]; [d->vs.func retain];
@ -3226,6 +3266,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Fragment: case QRhiShaderStage::Fragment:
d->fs.lib = lib; d->fs.lib = lib;
d->fs.func = func; d->fs.func = func;
if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
d->fs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->fs); rhiD->d->shaderCache.insert(shaderStage, d->fs);
[d->fs.lib retain]; [d->fs.lib retain];
[d->fs.func retain]; [d->fs.func retain];
@ -3360,8 +3402,9 @@ bool QMetalComputePipeline::build()
const QShader shader = m_shaderStage.shader(); const QShader shader = m_shaderStage.shader();
QString error; QString error;
QByteArray entryPoint; QByteArray entryPoint;
QShaderKey activeKey;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(), id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
&error, &entryPoint); &error, &entryPoint, &activeKey);
if (!lib) { if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error)); qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false; return false;
@ -3375,6 +3418,8 @@ bool QMetalComputePipeline::build()
d->cs.lib = lib; d->cs.lib = lib;
d->cs.func = func; d->cs.func = func;
d->cs.localSize = shader.description().computeShaderLocalSize(); d->cs.localSize = shader.description().computeShaderLocalSize();
if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
d->cs.nativeResourceBindingMap = *map;
if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) { if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
for (QMetalShader &s : rhiD->d->shaderCache) for (QMetalShader &s : rhiD->d->shaderCache)

View File

@ -433,10 +433,13 @@ public:
qsizetype *curOfs); qsizetype *curOfs);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD); void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, static const int SUPPORTED_STAGES = 3;
void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
QMetalCommandBuffer *cbD,
int dynamicOffsetCount, int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
bool offsetOnlyChange); bool offsetOnlyChange,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
int effectiveSampleCount(int sampleCount) const; int effectiveSampleCount(int sampleCount) const;
bool importedDevice = false; bool importedDevice = false;

View File

@ -214,7 +214,8 @@ QT_BEGIN_NAMESPACE
QShader, it indicates no shader code was found for the requested key. QShader, it indicates no shader code was found for the requested key.
*/ */
static const int QSB_VERSION = 1; static const int QSB_VERSION = 2;
static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
/*! /*!
Constructs a new, empty (and thus invalid) QShader instance. Constructs a new, empty (and thus invalid) QShader instance.
@ -345,6 +346,14 @@ void QShader::removeShader(const QShaderKey &key)
d->shaders.erase(it); d->shaders.erase(it);
} }
static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
{
*ds << k.source();
*ds << k.sourceVersion().version();
*ds << k.sourceVersion().flags();
*ds << k.sourceVariant();
}
/*! /*!
\return a serialized binary version of all the data held by the \return a serialized binary version of all the data held by the
QShader, suitable for writing to files or other I/O devices. QShader, suitable for writing to files or other I/O devices.
@ -365,18 +374,42 @@ QByteArray QShader::serialized() const
ds << d->shaders.count(); ds << d->shaders.count();
for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) { for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key()); const QShaderKey &k(it.key());
ds << k.source(); writeShaderKey(&ds, k);
ds << k.sourceVersion().version();
ds << k.sourceVersion().flags();
ds << k.sourceVariant();
const QShaderCode &shader(d->shaders.value(k)); const QShaderCode &shader(d->shaders.value(k));
ds << shader.shader(); ds << shader.shader();
ds << shader.entryPoint(); ds << shader.entryPoint();
} }
ds << d->bindings.count();
for (auto it = d->bindings.cbegin(), itEnd = d->bindings.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
writeShaderKey(&ds, k);
const NativeResourceBindingMap &map(it.value());
ds << map.count();
for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
ds << mapIt.key();
ds << mapIt.value().first;
ds << mapIt.value().second;
}
}
return qCompress(buf.buffer()); return qCompress(buf.buffer());
} }
static void readShaderKey(QDataStream *ds, QShaderKey *k)
{
int intVal;
*ds >> intVal;
k->setSource(QShader::Source(intVal));
QShaderVersion ver;
*ds >> intVal;
ver.setVersion(intVal);
*ds >> intVal;
ver.setFlags(QShaderVersion::Flags(intVal));
k->setSourceVersion(ver);
*ds >> intVal;
k->setSourceVariant(QShader::Variant(intVal));
}
/*! /*!
Creates a new QShader instance from the given \a data. Creates a new QShader instance from the given \a data.
@ -396,8 +429,11 @@ QShader QShader::fromSerialized(const QByteArray &data)
Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
int intVal; int intVal;
ds >> intVal; ds >> intVal;
if (intVal != QSB_VERSION) const int qsbVersion = intVal;
if (qsbVersion != QSB_VERSION && qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) {
qWarning("Attempted to deserialize QShader with unknown version %d.", qsbVersion);
return QShader(); return QShader();
}
ds >> intVal; ds >> intVal;
d->stage = Stage(intVal); d->stage = Stage(intVal);
@ -408,16 +444,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> count; ds >> count;
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
QShaderKey k; QShaderKey k;
ds >> intVal; readShaderKey(&ds, &k);
k.setSource(Source(intVal));
QShaderVersion ver;
ds >> intVal;
ver.setVersion(intVal);
ds >> intVal;
ver.setFlags(QShaderVersion::Flags(intVal));
k.setSourceVersion(ver);
ds >> intVal;
k.setSourceVariant(Variant(intVal));
QShaderCode shader; QShaderCode shader;
QByteArray s; QByteArray s;
ds >> s; ds >> s;
@ -427,6 +454,27 @@ QShader QShader::fromSerialized(const QByteArray &data)
d->shaders[k] = shader; d->shaders[k] = shader;
} }
if (qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) {
ds >> count;
for (int i = 0; i < count; ++i) {
QShaderKey k;
readShaderKey(&ds, &k);
NativeResourceBindingMap map;
int mapSize;
ds >> mapSize;
for (int b = 0; b < mapSize; ++b) {
int binding;
ds >> binding;
int firstNativeBinding;
ds >> firstNativeBinding;
int secondNativeBinding;
ds >> secondNativeBinding;
map.insert(binding, { firstNativeBinding, secondNativeBinding });
}
d->bindings.insert(k, map);
}
}
return bs; return bs;
} }
@ -460,7 +508,7 @@ bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
{ {
return lhs.d->stage == rhs.d->stage return lhs.d->stage == rhs.d->stage
&& lhs.d->shaders == rhs.d->shaders; && lhs.d->shaders == rhs.d->shaders;
// do not bother with desc, if the shader code is the same, the description must match too // do not bother with desc and bindings, if the shader code is the same, the description must match too
} }
/*! /*!
@ -586,4 +634,66 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
} }
#endif // QT_NO_DEBUG_STREAM #endif // QT_NO_DEBUG_STREAM
/*!
\typedef QShader::NativeResourceBindingMap
Synonym for QHash<int, QPair<int, int>>.
The resource binding model QRhi assumes is based on SPIR-V. This means that
uniform buffers, storage buffers, combined image samplers, and storage
images share a common binding point space. The binding numbers in
QShaderDescription and QRhiShaderResourceBinding are expected to match the
\c binding layout qualifier in the Vulkan-compatible GLSL shader.
Graphics APIs other than Vulkan may use a resource binding model that is
not fully compatible with this. In addition, the generator of the shader
code translated from SPIR-V may choose not to take the SPIR-V binding
qualifiers into account, for various reasons. (this is the case with the
Metal backend of SPIRV-Cross, for example).
Therefore, a QShader may expose an additional map that describes what the
native binding point for a given SPIR-V binding is. The QRhi backends are
expected to use this map automatically, as appropriate. The value is a
pair, because combined image samplers may map to two native resources (a
texture and a sampler) in some shading languages. In that case the second
value refers to the sampler.
*/
/*!
\return the native binding map for \a key or null if no extra mapping is
available, or is not applicable.
*/
const QShader::NativeResourceBindingMap *QShader::nativeResourceBindingMap(const QShaderKey &key) const
{
auto it = d->bindings.constFind(key);
if (it == d->bindings.cend())
return nullptr;
return &it.value();
}
/*!
Stores the given native resource binding \a map associated with \a key.
\sa nativeResourceBindingMap()
*/
void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
{
detach();
d->bindings[key] = map;
}
/*!
Removes the native resource binding map for \a key.
*/
void QShader::removeResourceBindingMap(const QShaderKey &key)
{
auto it = d->bindings.find(key);
if (it == d->bindings.end())
return;
detach();
d->bindings.erase(it);
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -149,6 +149,11 @@ public:
QByteArray serialized() const; QByteArray serialized() const;
static QShader fromSerialized(const QByteArray &data); static QShader fromSerialized(const QByteArray &data);
using NativeResourceBindingMap = QHash<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
const NativeResourceBindingMap *nativeResourceBindingMap(const QShaderKey &key) const;
void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
void removeResourceBindingMap(const QShaderKey &key);
private: private:
QShaderPrivate *d; QShaderPrivate *d;
friend struct QShaderPrivate; friend struct QShaderPrivate;

View File

@ -66,7 +66,8 @@ struct Q_GUI_EXPORT QShaderPrivate
: ref(1), : ref(1),
stage(other->stage), stage(other->stage),
desc(other->desc), desc(other->desc),
shaders(other->shaders) shaders(other->shaders),
bindings(other->bindings)
{ {
} }
@ -77,6 +78,7 @@ struct Q_GUI_EXPORT QShaderPrivate
QShader::Stage stage = QShader::VertexStage; QShader::Stage stage = QShader::VertexStage;
QShaderDescription desc; QShaderDescription desc;
QHash<QShaderKey, QShaderCode> shaders; QHash<QShaderKey, QShaderCode> shaders;
QHash<QShaderKey, QShader::NativeResourceBindingMap> bindings;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -2088,8 +2088,12 @@ void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *pain
tl->draw(painter, offset, selections, context.clip.isValid() ? (context.clip & clipRect) : clipRect); tl->draw(painter, offset, selections, context.clip.isValid() ? (context.clip & clipRect) : clipRect);
if ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen) // if the block is empty and it precedes a table, do not draw the cursor.
|| (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty())) { // the cursor is drawn later after the table has been drawn so no need
// to draw it here.
if (!isEmptyBlockBeforeTable(frameIteratorForTextPosition(blpos))
&& ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
|| (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty()))) {
int cpos = context.cursorPosition; int cpos = context.cursorPosition;
if (cpos < -1) if (cpos < -1)
cpos = tl->preeditAreaPosition() - (cpos + 2); cpos = tl->preeditAreaPosition() - (cpos + 2);

View File

@ -2125,22 +2125,7 @@ void QTextEngine::itemize() const
} }
#if QT_CONFIG(harfbuzz) #if QT_CONFIG(harfbuzz)
analysis = scriptAnalysis.data(); analysis = scriptAnalysis.data();
if (qt_useHarfbuzzNG()) { if (!qt_useHarfbuzzNG()) {
// ### pretend HB-old behavior for now
for (int i = 0; i < length; ++i) {
switch (analysis[i].script) {
case QChar::Script_Latin:
case QChar::Script_Hiragana:
case QChar::Script_Katakana:
case QChar::Script_Bopomofo:
case QChar::Script_Han:
analysis[i].script = QChar::Script_Common;
break;
default:
break;
}
}
} else {
for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
analysis[i].script = hbscript_to_script(script_to_hbscript(analysis[i].script)); analysis[i].script = hbscript_to_script(script_to_hbscript(analysis[i].script));
} }
@ -3619,7 +3604,12 @@ int QTextEngine::positionInLigature(const QScriptItem *si, int end,
int clusterLength = 0; int clusterLength = 0;
if (si->analysis.script != QChar::Script_Common && if (si->analysis.script != QChar::Script_Common &&
si->analysis.script != QChar::Script_Greek) { si->analysis.script != QChar::Script_Greek &&
si->analysis.script != QChar::Script_Latin &&
si->analysis.script != QChar::Script_Hiragana &&
si->analysis.script != QChar::Script_Katakana &&
si->analysis.script != QChar::Script_Bopomofo &&
si->analysis.script != QChar::Script_Han) {
if (glyph_pos == -1) if (glyph_pos == -1)
return si->position + end; return si->position + end;
else { else {

View File

@ -431,6 +431,53 @@ QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
return alpnString; return alpnString;
} }
#endif // SUPPORTS_ALPN #endif // SUPPORTS_ALPN
qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket)
{
Q_ASSERT(plainSocket);
static const qint64 shrinkCutoff = 1024 * 12;
static const qint64 defaultRead = 1024 * 16;
qint64 bytesRead = 0;
const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable());
if (toRead > 0) {
const auto bufferSize = buffer.size();
buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in
buffer.resize(bufferSize + toRead);
bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead);
buffer.resize(bufferSize + bytesRead);
// In case of excessive memory usage we shrink:
if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead)
buffer.shrink_to_fit();
}
return bytesRead;
}
void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
{
Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA);
if (int(secBuffer.cbBuffer) >= buffer.size())
return;
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "We got SECBUFFER_EXTRA, will retain %lu bytes", secBuffer.cbBuffer);
#endif
std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
buffer.resize(secBuffer.cbBuffer);
}
qint64 checkIncompleteData(const SecBuffer &secBuffer)
{
if (secBuffer.BufferType == SECBUFFER_MISSING) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "Need %lu more bytes.", secBuffer.cbBuffer);
#endif
return secBuffer.cbBuffer;
}
return 0;
}
} // anonymous namespace } // anonymous namespace
bool QSslSocketPrivate::s_loadRootCertsOnDemand = true; bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
@ -619,8 +666,8 @@ bool QSslSocketBackendPrivate::acquireCredentialsHandle()
nullptr); nullptr);
if (!chainContext) { if (!chainContext) {
const QString message = isClient const QString message = isClient
? QSslSocket::tr("The certificate provided cannot be used for a client.") ? QSslSocket::tr("The certificate provided cannot be used for a client.")
: QSslSocket::tr("The certificate provided cannot be used for a server."); : QSslSocket::tr("The certificate provided cannot be used for a server.");
setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message); setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
return false; return false;
} }
@ -774,7 +821,11 @@ bool QSslSocketBackendPrivate::acceptContext()
Q_ASSERT(mode == QSslSocket::SslServerMode); Q_ASSERT(mode == QSslSocket::SslServerMode);
ULONG contextReq = getContextRequirements(); ULONG contextReq = getContextRequirements();
intermediateBuffer += plainSocket->read(16384); if (missingData > plainSocket->bytesAvailable())
return true;
missingData = 0;
readToBuffer(intermediateBuffer, plainSocket);
if (intermediateBuffer.isEmpty()) if (intermediateBuffer.isEmpty())
return true; // definitely need more data.. return true; // definitely need more data..
@ -830,6 +881,7 @@ bool QSslSocketBackendPrivate::acceptContext()
if (status == SEC_E_INCOMPLETE_MESSAGE) { if (status == SEC_E_INCOMPLETE_MESSAGE) {
// Need more data // Need more data
missingData = checkIncompleteData(outBuffers[0]);
return true; return true;
} }
@ -837,9 +889,9 @@ bool QSslSocketBackendPrivate::acceptContext()
// https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
// inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
// be stored. // be stored.
intermediateBuffer = intermediateBuffer.right(int(inBuffers[1].cbBuffer)); retainExtraData(intermediateBuffer, inBuffers[1]);
} else { /* No 'extra' data, message not incomplete */ } else { /* No 'extra' data, message not incomplete */
intermediateBuffer.clear(); intermediateBuffer.resize(0);
} }
if (status != SEC_I_CONTINUE_NEEDED) { if (status != SEC_I_CONTINUE_NEEDED) {
@ -865,11 +917,15 @@ bool QSslSocketBackendPrivate::performHandshake()
Q_ASSERT(schannelState == SchannelState::PerformHandshake); Q_ASSERT(schannelState == SchannelState::PerformHandshake);
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "Bytes available from socket:" << plainSocket->bytesAvailable(); qCDebug(lcSsl, "Bytes available from socket: %lld", plainSocket->bytesAvailable());
qCDebug(lcSsl) << "intermediateBuffer size:" << intermediateBuffer.size(); qCDebug(lcSsl, "intermediateBuffer size: %d", intermediateBuffer.size());
#endif #endif
intermediateBuffer += plainSocket->read(16384); if (missingData > plainSocket->bytesAvailable())
return true;
missingData = 0;
readToBuffer(intermediateBuffer, plainSocket);
if (intermediateBuffer.isEmpty()) if (intermediateBuffer.isEmpty())
return true; // no data, will fail return true; // no data, will fail
@ -918,11 +974,10 @@ bool QSslSocketBackendPrivate::performHandshake()
// https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
// inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
// be stored. // be stored.
intermediateBuffer = intermediateBuffer.right(int(inputBuffers[1].cbBuffer)); retainExtraData(intermediateBuffer, inputBuffers[1]);
} else { } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
// Clear the buffer if we weren't asked for more data // Clear the buffer if we weren't asked for more data
if (status != SEC_E_INCOMPLETE_MESSAGE) intermediateBuffer.resize(0);
intermediateBuffer.clear();
} }
switch (status) { switch (status) {
case SEC_E_OK: case SEC_E_OK:
@ -955,6 +1010,7 @@ bool QSslSocketBackendPrivate::performHandshake()
return true; return true;
case SEC_E_INCOMPLETE_MESSAGE: case SEC_E_INCOMPLETE_MESSAGE:
// Simply incomplete, wait for more data // Simply incomplete, wait for more data
missingData = checkIncompleteData(outBuffers[0]);
return true; return true;
case SEC_E_ALGORITHM_MISMATCH: case SEC_E_ALGORITHM_MISMATCH:
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
@ -1157,6 +1213,8 @@ void QSslSocketBackendPrivate::reset()
connectionEncrypted = false; connectionEncrypted = false;
shutdown = false; shutdown = false;
renegotiating = false; renegotiating = false;
missingData = 0;
} }
void QSslSocketBackendPrivate::startClientEncryption() void QSslSocketBackendPrivate::startClientEncryption()
@ -1228,8 +1286,7 @@ void QSslSocketBackendPrivate::transmit()
fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer); fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
const qint64 bytesWritten = plainSocket->write(fullMessage); const qint64 bytesWritten = plainSocket->write(fullMessage);
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "Wrote" << bytesWritten << "of total" qCDebug(lcSsl, "Wrote %lld of total %d bytes", bytesWritten, fullMessage.length());
<< fullMessage.length() << "bytes";
#endif #endif
if (bytesWritten >= 0) { if (bytesWritten >= 0) {
totalBytesWritten += bytesWritten; totalBytesWritten += bytesWritten;
@ -1254,36 +1311,32 @@ void QSslSocketBackendPrivate::transmit()
int totalRead = 0; int totalRead = 0;
bool hadIncompleteData = false; bool hadIncompleteData = false;
while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) { while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
QByteArray ciphertext; if (missingData > plainSocket->bytesAvailable()) {
if (intermediateBuffer.length()) {
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "Restoring data from intermediateBuffer:" qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData);
<< intermediateBuffer.length() << "bytes";
#endif #endif
ciphertext.swap(intermediateBuffer); break;
} }
int initialLength = ciphertext.length();
ciphertext += plainSocket->read(16384); missingData = 0;
const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "Read" << ciphertext.length() - initialLength qCDebug(lcSsl, "Read %lld encrypted bytes from the socket", bytesRead);
<< "encrypted bytes from the socket";
#endif #endif
if (ciphertext.length() == 0 || (hadIncompleteData && initialLength == ciphertext.length())) { if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << (hadIncompleteData ? "No new data received, leaving loop!" qCDebug(lcSsl, (hadIncompleteData ? "No new data received, leaving loop!"
: "Nothing to decrypt, leaving loop!"); : "Nothing to decrypt, leaving loop!"));
#endif #endif
if (ciphertext.length()) // We have data, it came from intermediateBuffer, swap back
intermediateBuffer.swap(ciphertext);
break; break;
} }
hadIncompleteData = false; hadIncompleteData = false;
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "Total amount of bytes to decrypt:" << ciphertext.length(); qCDebug(lcSsl, "Total amount of bytes to decrypt: %d", intermediateBuffer.length());
#endif #endif
SecBuffer dataBuffer[4]{ SecBuffer dataBuffer[4]{
createSecBuffer(ciphertext, SECBUFFER_DATA), createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY) createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
@ -1299,34 +1352,30 @@ void QSslSocketBackendPrivate::transmit()
if (dataBuffer[1].cbBuffer > 0) { if (dataBuffer[1].cbBuffer > 0) {
// It is always decrypted in-place. // It is always decrypted in-place.
// But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER. // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
// The pointers in all of those still point into the 'ciphertext' byte array. // The pointers in all of those still point into 'intermediateBuffer'.
buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer), buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
dataBuffer[1].cbBuffer); dataBuffer[1].cbBuffer);
totalRead += dataBuffer[1].cbBuffer; totalRead += dataBuffer[1].cbBuffer;
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "Decrypted" << dataBuffer[1].cbBuffer qCDebug(lcSsl, "Decrypted %lu bytes. New read buffer size: %d",
<< "bytes. New read buffer size:" << buffer.size(); dataBuffer[1].cbBuffer, buffer.size());
#endif #endif
} }
if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) { if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
// https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
// dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed, // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
// the rest need to be stored. // the rest need to be stored.
#ifdef QSSLSOCKET_DEBUG retainExtraData(intermediateBuffer, dataBuffer[3]);
qCDebug(lcSsl) << "We've got excess data, moving it to the intermediate buffer:" } else {
<< dataBuffer[3].cbBuffer << "bytes"; intermediateBuffer.resize(0);
#endif
intermediateBuffer = ciphertext.right(int(dataBuffer[3].cbBuffer));
} }
} }
if (status == SEC_E_INCOMPLETE_MESSAGE) { if (status == SEC_E_INCOMPLETE_MESSAGE) {
// Need more data before we can decrypt.. to the buffer it goes! missingData = checkIncompleteData(dataBuffer[0]);
#ifdef QSSLSOCKET_DEBUG #ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!"); qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!");
#endif #endif
Q_ASSERT(intermediateBuffer.isEmpty());
intermediateBuffer.swap(ciphertext);
// We try again, but if we don't get any more data then we leave // We try again, but if we don't get any more data then we leave
hadIncompleteData = true; hadIncompleteData = true;
} else if (status == SEC_E_INVALID_HANDLE) { } else if (status == SEC_E_INVALID_HANDLE) {

View File

@ -145,6 +145,7 @@ private:
const CERT_CONTEXT *localCertContext = nullptr; const CERT_CONTEXT *localCertContext = nullptr;
ULONG contextAttributes = 0; ULONG contextAttributes = 0;
qint64 missingData = 0;
bool renegotiating = false; bool renegotiating = false;
}; };

View File

@ -60,6 +60,12 @@
#include "qopenglextensions.h" #include "qopenglextensions.h"
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QAbstractOpenGLExtension::~QAbstractOpenGLExtension() QAbstractOpenGLExtension::~QAbstractOpenGLExtension()
@ -7720,3 +7726,6 @@ bool QOpenGLExtension_QCOM_tiled_rendering::initializeOpenGLFunctions()
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif

View File

@ -66,6 +66,12 @@
#include <QtGui/qopengl.h> #include <QtGui/qopengl.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QOpenGLContext; class QOpenGLContext;
@ -19473,6 +19479,10 @@ inline void QOpenGLExtension_QCOM_tiled_rendering::glEndTilingQCOM(GLbitfield pr
QT_END_NAMESPACE QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL #endif // QT_NO_OPENGL
#endif #endif

View File

@ -195,20 +195,6 @@ namespace QtAndroidInput
angleDelta); angleDelta);
} }
void releaseMouse(int x, int y)
{
m_ignoreMouseEvents = true;
QPoint globalPos(x,y);
QWindow *tlw = topLevelWindowAt(globalPos);
QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos;
// Release left button
QWindowSystemInterface::handleMouseEvent(tlw,
localPos,
globalPos,
Qt::MouseButtons(Qt::NoButton));
}
static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
{ {
QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext(); QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext();

View File

@ -61,8 +61,6 @@ namespace QtAndroidInput
void updateHandles(int handleCount, QPoint editMenuPos = QPoint(), uint32_t editButtons = 0, QPoint cursor = QPoint(), QPoint anchor = QPoint(), bool rtl = false); void updateHandles(int handleCount, QPoint editMenuPos = QPoint(), uint32_t editButtons = 0, QPoint cursor = QPoint(), QPoint anchor = QPoint(), bool rtl = false);
bool registerNatives(JNIEnv *env); bool registerNatives(JNIEnv *env);
void releaseMouse(int x, int y);
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -827,9 +827,6 @@ void QAndroidInputContext::longPress(int x, int y)
focusObjectStopComposing(); focusObjectStopComposing();
// Release left button, otherwise the following events will cancel the menu popup
QtAndroidInput::releaseMouse(x, y);
const double pixelDensity = const double pixelDensity =
QGuiApplication::focusWindow() QGuiApplication::focusWindow()
? QHighDpiScaling::factor(QGuiApplication::focusWindow()) ? QHighDpiScaling::factor(QGuiApplication::focusWindow())

View File

@ -88,10 +88,13 @@
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <qwindowdefs.h> #include <qwindowdefs.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application");
QT_END_NAMESPACE
QT_USE_NAMESPACE QT_USE_NAMESPACE
@implementation QCocoaApplicationDelegate { @implementation QCocoaApplicationDelegate {
bool startedQuit;
NSObject <NSApplicationDelegate> *reflectionDelegate; NSObject <NSApplicationDelegate> *reflectionDelegate;
bool inLaunch; bool inLaunch;
} }
@ -140,46 +143,30 @@ QT_USE_NAMESPACE
return [[self.dockMenu retain] autorelease]; return [[self.dockMenu retain] autorelease];
} }
- (BOOL)canQuit
{
QCloseEvent ev;
QGuiApplication::sendEvent(qGuiApp, &ev);
return ev.isAccepted();
}
// This function will only be called when NSApp is actually running. // This function will only be called when NSApp is actually running.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{ {
if ([reflectionDelegate respondsToSelector:_cmd]) if ([reflectionDelegate respondsToSelector:_cmd])
return [reflectionDelegate applicationShouldTerminate:sender]; return [reflectionDelegate applicationShouldTerminate:sender];
if ([self canQuit]) {
if (!startedQuit) {
startedQuit = true;
// Close open windows. This is done in order to deliver de-expose
// events while the event loop is still running.
const QWindowList topLevels = QGuiApplication::topLevelWindows();
for (int i = 0; i < topLevels.size(); ++i) {
QWindow *topLevelWindow = topLevels.at(i);
// Already closed windows will not have a platform window, skip those
if (topLevelWindow->handle())
QWindowSystemInterface::handleCloseEvent(topLevelWindow);
}
QWindowSystemInterface::flushWindowSystemEvents();
QGuiApplication::exit(0);
startedQuit = false;
}
}
if (QGuiApplicationPrivate::instance()->threadData->eventLoops.isEmpty()) { if (QGuiApplicationPrivate::instance()->threadData->eventLoops.isEmpty()) {
// INVARIANT: No event loop is executing. This probably // No event loop is executing. This probably means that Qt is used as a plugin,
// means that Qt is used as a plugin, or as a part of a native // or as a part of a native Cocoa application. In any case it should be fine to
// Cocoa application. In any case it should be fine to // terminate now.
// terminate now: qCDebug(lcQpaApplication) << "No running event loops, terminating now";
return NSTerminateNow; return NSTerminateNow;
} }
if (!QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>()) {
qCDebug(lcQpaApplication) << "Application termination canceled";
return NSTerminateCancel;
}
// Even if the application termination was accepted by the application we can't
// return NSTerminateNow, as that would trigger AppKit to ultimately call exit().
// We need to ensure that the runloop continues spinning so that we can return
// from our own event loop back to main(), and exit from there.
qCDebug(lcQpaApplication) << "Termination accepted, but returning to runloop for exit through main()";
return NSTerminateCancel; return NSTerminateCancel;
} }

View File

@ -50,6 +50,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QCocoaWindow;
class QCocoaGLContext : public QPlatformOpenGLContext class QCocoaGLContext : public QPlatformOpenGLContext
{ {
public: public:
@ -76,12 +78,12 @@ private:
static NSOpenGLPixelFormat *pixelFormatForSurfaceFormat(const QSurfaceFormat &format); static NSOpenGLPixelFormat *pixelFormatForSurfaceFormat(const QSurfaceFormat &format);
bool setDrawable(QPlatformSurface *surface); bool setDrawable(QPlatformSurface *surface);
void prepareDrawable(QCocoaWindow *platformWindow);
void updateSurfaceFormat(); void updateSurfaceFormat();
NSOpenGLContext *m_context = nil; NSOpenGLContext *m_context = nil;
NSOpenGLContext *m_shareContext = nil; NSOpenGLContext *m_shareContext = nil;
QSurfaceFormat m_format; QSurfaceFormat m_format;
bool m_didCheckForSoftwareContext = false;
QVarLengthArray<QMacNotificationObserver, 3> m_updateObservers; QVarLengthArray<QMacNotificationObserver, 3> m_updateObservers;
QAtomicInt m_needsUpdate = false; QAtomicInt m_needsUpdate = false;
}; };

View File

@ -223,12 +223,10 @@ NSOpenGLPixelFormat *QCocoaGLContext::pixelFormatForSurfaceFormat(const QSurface
attrs << NSOpenGLPFAAllowOfflineRenderers; attrs << NSOpenGLPFAAllowOfflineRenderers;
} }
// FIXME: Pull this information out of the NSView if (qGuiApp->testAttribute(Qt::AA_UseSoftwareOpenGL)) {
QByteArray useLayer = qgetenv("QT_MAC_WANTS_LAYER"); // kCGLRendererGenericFloatID is the modern software renderer on macOS,
if (!useLayer.isEmpty() && useLayer.toInt() > 0) { // as opposed to kCGLRendererGenericID, which is deprecated.
// Disable the software rendering fallback. This makes compositing attrs << NSOpenGLPFARendererID << kCGLRendererGenericFloatID;
// OpenGL and raster NSViews using Core Animation layers possible.
attrs << NSOpenGLPFANoRecovery;
} }
attrs << 0; // 0-terminate array attrs << 0; // 0-terminate array
@ -368,23 +366,6 @@ bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
[m_context makeCurrentContext]; [m_context makeCurrentContext];
if (surface->surface()->surfaceClass() == QSurface::Window) { if (surface->surface()->surfaceClass() == QSurface::Window) {
// Disable high-resolution surfaces when using the software renderer, which has the
// problem that the system silently falls back to a to using a low-resolution buffer
// when a high-resolution buffer is requested. This is not detectable using the NSWindow
// convertSizeToBacking and backingScaleFactor APIs. A typical result of this is that Qt
// will display a quarter of the window content when running in a virtual machine.
if (!m_didCheckForSoftwareContext) {
// FIXME: This ensures we check only once per context,
// but the context may be used for multiple surfaces.
m_didCheckForSoftwareContext = true;
const GLubyte* renderer = glGetString(GL_RENDERER);
if (qstrcmp((const char *)renderer, "Apple Software Renderer") == 0) {
NSView *view = static_cast<QCocoaWindow *>(surface)->m_view;
[view setWantsBestResolutionOpenGLSurface:NO];
}
}
if (m_needsUpdate.fetchAndStoreRelaxed(false)) if (m_needsUpdate.fetchAndStoreRelaxed(false))
update(); update();
} }
@ -413,11 +394,14 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
} }
Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window); Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(surface)->view()); auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
QNSView *view = qnsview_cast(cocoaWindow->view());
if (view == m_context.view) if (view == m_context.view)
return true; return true;
prepareDrawable(cocoaWindow);
// Setting the drawable may happen on a separate thread as a result of // Setting the drawable may happen on a separate thread as a result of
// a call to makeCurrent, so we need to set up the observers before we // a call to makeCurrent, so we need to set up the observers before we
// associate the view with the context. That way we will guarantee that // associate the view with the context. That way we will guarantee that
@ -460,6 +444,30 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
return true; return true;
} }
void QCocoaGLContext::prepareDrawable(QCocoaWindow *platformWindow)
{
// We generally want high-DPI GL surfaces, unless the user has explicitly disabled them
bool prefersBestResolutionOpenGLSurface = qt_mac_resolveOption(YES,
platformWindow->window(), "_q_mac_wantsBestResolutionOpenGLSurface",
"QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
auto *view = platformWindow->view();
// The only case we have to opt out ourselves is when using the Apple software renderer
// in combination with surface-backed views, as these together do not support high-DPI.
if (prefersBestResolutionOpenGLSurface) {
int rendererID = 0;
[m_context getValues:&rendererID forParameter:NSOpenGLContextParameterCurrentRendererID];
bool isSoftwareRenderer = (rendererID & kCGLRendererIDMatchingMask) == kCGLRendererGenericFloatID;
if (isSoftwareRenderer && !view.layer) {
qCInfo(lcQpaOpenGLContext) << "Disabling high resolution GL surface due to software renderer";
prefersBestResolutionOpenGLSurface = false;
}
}
view.wantsBestResolutionOpenGLSurface = prefersBestResolutionOpenGLSurface;
}
// NSOpenGLContext is not re-entrant. Even when using separate contexts per thread, // NSOpenGLContext is not re-entrant. Even when using separate contexts per thread,
// view, and window, calls into the API will still deadlock. For more information // view, and window, calls into the API will still deadlock. For more information
// see https://openradar.appspot.com/37064579 // see https://openradar.appspot.com/37064579
@ -491,6 +499,21 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
return; return;
} }
if (m_context.view.layer) {
// Flushing an NSOpenGLContext will hit the screen immediately, ignoring
// any Core Animation transactions in place. This may result in major
// visual artifacts if the flush happens out of sync with the size
// of the layer, view, and window reflected by other parts of the UI,
// e.g. if the application flushes in the resize event or a timer during
// window resizing, instead of in the expose event.
auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)."
<< "Skipping flush to avoid visual artifacts.";
return;
}
}
QMutexLocker locker(&s_reentrancyMutex); QMutexLocker locker(&s_reentrancyMutex);
[m_context flushBuffer]; [m_context flushBuffer];
} }

View File

@ -44,17 +44,6 @@
- (void)initDrawing - (void)initDrawing
{ {
[self updateLayerBacking]; [self updateLayerBacking];
// Enable high-DPI OpenGL for retina displays. Enabling has the side
// effect that Cocoa will start calling glViewport(0, 0, width, height),
// overriding any glViewport calls in application code. This is usually not a
// problem, except if the application wants to have a "custom" viewport.
// (like the hellogl example)
if (m_platformWindow->window()->supportsOpenGL()) {
self.wantsBestResolutionOpenGLSurface = qt_mac_resolveOption(YES, m_platformWindow->window(),
"_q_mac_wantsBestResolutionOpenGLSurface", "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
// See also QCocoaGLContext::makeCurrent for software renderer workarounds.
}
} }
- (BOOL)isOpaque - (BOOL)isOpaque

View File

@ -59,6 +59,8 @@ QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode, const QString &devi
QString id = deviceId; QString id = deviceId;
if (id.isEmpty()) if (id.isEmpty())
id = QCocoaPrinterSupport().defaultPrintDeviceId(); id = QCocoaPrinterSupport().defaultPrintDeviceId();
else
setProperty(QPrintEngine::PPK_PrinterName, deviceId);
d->m_printDevice.reset(new QCocoaPrintDevice(id)); d->m_printDevice.reset(new QCocoaPrintDevice(id));
d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize()); d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize());
d->initialize(); d->initialize();

View File

@ -42,6 +42,8 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QFileOpenEvent> #include <QFileOpenEvent>
#include <qpa/qwindowsysteminterface.h>
#include <Entry.h> #include <Entry.h>
#include <Path.h> #include <Path.h>
@ -52,8 +54,7 @@ QHaikuApplication::QHaikuApplication(const char *signature)
bool QHaikuApplication::QuitRequested() bool QHaikuApplication::QuitRequested()
{ {
QEvent quitEvent(QEvent::Quit); QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
QCoreApplication::sendEvent(QCoreApplication::instance(), &quitEvent);
return true; return true;
} }

View File

@ -42,6 +42,8 @@
#ifndef QT_NO_SESSIONMANAGER #ifndef QT_NO_SESSIONMANAGER
#include <qpa/qwindowsysteminterface.h>
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qdatetime.h> #include <qdatetime.h>
#include <qfileinfo.h> #include <qfileinfo.h>
@ -289,8 +291,7 @@ static void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
if (smcConn != smcConnection) if (smcConn != smcConnection)
return; return;
resetSmState(); resetSmState();
QEvent quitEvent(QEvent::Quit); QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
QGuiApplication::sendEvent(qApp, &quitEvent);
} }
static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData) static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)

View File

@ -221,18 +221,18 @@ public:
int disconnectCount; int disconnectCount;
bool hasSQLFetchScroll; bool hasSQLFetchScroll;
bool isStmtHandleValid(); bool isStmtHandleValid() const;
void updateStmtHandleState(); void updateStmtHandleState();
}; };
bool QODBCResultPrivate::isStmtHandleValid() bool QODBCResultPrivate::isStmtHandleValid() const
{ {
return disconnectCount == drv_d_func()->disconnectCount; return drv_d_func() && disconnectCount == drv_d_func()->disconnectCount;
} }
void QODBCResultPrivate::updateStmtHandleState() void QODBCResultPrivate::updateStmtHandleState()
{ {
disconnectCount = drv_d_func()->disconnectCount; disconnectCount = drv_d_func() ? drv_d_func()->disconnectCount : 0;
} }
static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0) static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0)
@ -975,7 +975,7 @@ QODBCResult::QODBCResult(const QODBCDriver *db)
QODBCResult::~QODBCResult() QODBCResult::~QODBCResult()
{ {
Q_D(QODBCResult); Q_D(QODBCResult);
if (d->hStmt && d->isStmtHandleValid() && driver()->isOpen()) { if (d->hStmt && d->isStmtHandleValid() && driver() && driver()->isOpen()) {
SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS) if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")

View File

@ -1369,7 +1369,7 @@ QSqlRecord QSqlTableModel::record(int row) const
// get generated flags from the cache // get generated flags from the cache
const QSqlTableModelPrivate::ModifiedRow mrow = d->cache.value(row); const QSqlTableModelPrivate::ModifiedRow mrow = d->cache.value(row);
if (mrow.op() != QSqlTableModelPrivate::None) { if (mrow.op() != QSqlTableModelPrivate::None) {
const QSqlRecord crec = mrow.rec(); const QSqlRecord &crec = mrow.rec();
for (int i = 0, cnt = rec.count(); i < cnt; ++i) for (int i = 0, cnt = rec.count(); i < cnt; ++i)
rec.setGenerated(i, crec.isGenerated(i)); rec.setGenerated(i, crec.isGenerated(i));
} }

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the documentation of the Qt Toolkit. ** This file is part of the documentation of the Qt Toolkit.
@ -296,3 +296,31 @@ QTest::keyClick(myWindow, Qt::Key_Escape, Qt::ShiftModifier, 200);
} }
//! [30]
void TestQLocale::initTestCase_data()
{
QTest::addColumn<QLocale>("locale");
QTest::newRow("C") << QLocale::c();
QTest::newRow("UKish") << QLocale("en_GB");
QTest::newRow("USAish") << QLocale(QLocale::English);
}
void TestQLocale::roundTripInt_data()
{
QTest::addColumn<int>("number");
QTest::newRow("one") << 1;
QTest::newRow("two") << 2;
QTest::newRow("ten") << 10;
}
//! [30]
//! [31]
void TestQLocale::roundTripInt()
{
QFETCH_GLOBAL(QLocale, locale);
QFETCH(int, number);
bool ok;
QCOMPARE(locale.toInt(locale.toString(number), &ok), number);
QVERIFY(ok);
}
//! [31]

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation. ** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
@ -89,12 +89,14 @@
private slot is a test function in your test. QTest::qExec() can be used to execute private slot is a test function in your test. QTest::qExec() can be used to execute
all test functions in the test object. all test functions in the test object.
In addition, there are four private slots that are \e not treated as test functions. In addition, you can define the following private slots that are \e not
They will be executed by the testing framework and can be used to initialize and treated as test functions. When present, they will be executed by the
clean up either the entire test or the current test function. testing framework and can be used to initialize and clean up either the
entire test or the current test function.
\list \list
\li \c{initTestCase()} will be called before the first test function is executed. \li \c{initTestCase()} will be called before the first test function is executed.
\li \c{initTestCase_data()} will be called to create a global test data table.
\li \c{cleanupTestCase()} will be called after the last test function was executed. \li \c{cleanupTestCase()} will be called after the last test function was executed.
\li \c{init()} will be called before each test function is executed. \li \c{init()} will be called before each test function is executed.
\li \c{cleanup()} will be called after every test function. \li \c{cleanup()} will be called after every test function.
@ -358,18 +360,44 @@
counters can be obtained by running any benchmark executable with the counters can be obtained by running any benchmark executable with the
option \c -perfcounterlist. option \c -perfcounterlist.
\list \note
\li \b Notes:
\list \list
\li Using the performance counter may require enabling access to non-privileged \li Using the performance counter may require enabling access to non-privileged
applications. applications.
\li Devices that do not support high-resolution timers default to \li Devices that do not support high-resolution timers default to
one-millisecond granularity. one-millisecond granularity.
\endlist \endlist
\endlist
See \l {Chapter 5: Writing a Benchmark}{Writing a Benchmark} in the Qt Test See \l {Chapter 5: Writing a Benchmark}{Writing a Benchmark} in the Qt Test
Tutorial for more benchmarking examples. Tutorial for more benchmarking examples.
\section1 Using Global Test Data
You can define \c{initTestCase_data()} to set up a global test data table.
Each test is run once for each row in the global test data table. When the
test function itself \l{Chapter 2: Data-driven Testing}{is data-driven},
it is run for each local data row, for each global data row. So, if there
are \c g rows in the global data table and \c d rows in the test's own
data-table, the number of runs of this test is \c g times \c d.
Global data is fetched from the table using the \l QFETCH_GLOBAL() macro.
The following are typical use cases for global test data:
\list
\li Selecting among the available database backends in QSql tests to run
every test against every database.
\li Doing all networking tests with and without SSL (HTTP versus HTTPS)
and proxying.
\li Testing a timer with a high precision clock and with a coarse one.
\li Selecting whether a parser shall read from a QByteArray or from a
QIODevice.
\endlist
For example, to test each number provided by \c {roundTripInt_data()} with
each locale provided by \c {initTestCase_data()}:
\snippet code/src_qtestlib_qtestcase.cpp 31
*/ */
/*! /*!
@ -513,10 +541,9 @@
QTest::newRow() function. Each set of data will become a QTest::newRow() function. Each set of data will become a
separate row in the test table. separate row in the test table.
\l QTest::newRow() takes one argument: a name that will be \l QTest::newRow() takes one argument: a name that will be associated
associated with the data set. If the test fails, the name will be with the data set and used in the test log to identify the data set.
used in the test log, referencing the failed data. Then we Then we stream the data set into the new table row. First an arbitrary
stream the data set into the new table row. First an arbitrary
string, and then the expected result of applying the string, and then the expected result of applying the
QString::toUpper() function to that string. QString::toUpper() function to that string.
@ -548,6 +575,10 @@
\li HELLO \li HELLO
\endtable \endtable
When data is streamed into the row, each datum is asserted to match
the type of the column whose value it supplies. If any assertion fails,
the test is aborted.
\section1 Rewriting the Test Function \section1 Rewriting the Test Function
Our test function can now be rewritten: Our test function can now be rewritten:

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the documentation of the Qt Toolkit. ** This file is part of the documentation of the Qt Toolkit.
@ -48,7 +48,7 @@
\snippet code/doc_src_qsignalspy.cpp 1 \snippet code/doc_src_qsignalspy.cpp 1
\b {Note:} Non-standard data types need to be registered, using \note Non-standard data types need to be registered, using
the qRegisterMetaType() function, before you can create a the qRegisterMetaType() function, before you can create a
QSignalSpy. For example: QSignalSpy. For example:
@ -57,6 +57,18 @@
To retrieve the instance, you can use qvariant_cast: To retrieve the instance, you can use qvariant_cast:
\snippet code/doc_src_qsignalspy.cpp 3 \snippet code/doc_src_qsignalspy.cpp 3
\section1 Verifying Signal Emissions
The QSignalSpy class provides an elegant mechanism for capturing the list
of signals emitted by an object. However, you should verify its validity
after construction. The constructor does a number of sanity checks, such as
verifying that the signal to be spied upon actually exists. To make the
diagnosis of test failures easier, the results of these checks should be
checked by calling \c QVERIFY(spy.isValid()) before proceeding further with
a test.
\sa QVERIFY()
*/ */
/*! \fn QSignalSpy::QSignalSpy(const QObject *object, const char *signal) /*! \fn QSignalSpy::QSignalSpy(const QObject *object, const char *signal)

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the documentation of the Qt Toolkit. ** This file is part of the documentation of the Qt Toolkit.
@ -220,8 +220,8 @@
\relates QTest \relates QTest
The fetch macro creates a local variable named \a name with the type \a type The fetch macro creates a local variable named \a name with the type \a type
on the stack. \a name has to match the element name from the test's data. on the stack. The \a name and \a type must match a column from the test's
If no such element exists, the test will assert. data table. This is asserted and the test will abort if the assertion fails.
Assuming a test has the following data: Assuming a test has the following data:
@ -235,10 +235,37 @@
\c aString and \c expected are variables on the stack that are initialized with \c aString and \c expected are variables on the stack that are initialized with
the current test data. the current test data.
\b {Note:} This macro can only be used in a test function that is invoked \note This macro can only be used in a test function that is invoked
by the test framework. The test function must have a _data function. by the test framework. The test function must have a _data function.
*/ */
/*! \macro QFETCH_GLOBAL(type, name)
\relates QTest
This macro fetches a variable named \a name with the type \a type from
a row in the global data table. The \a name and \a type must match a
column in the global data table. This is asserted and the test will abort
if the assertion fails.
Assuming a test has the following data:
\snippet code/src_qtestlib_qtestcase.cpp 30
The test's own data is a single number per row. In this case,
\c initTestCase_data() also supplies a locale per row. Therefore,
this test will be run with every combination of locale from the
latter and number from the former.
\snippet code/src_qtestlib_qtestcase.cpp 31
The locale is read from the global data table using QFETCH_GLOBAL(),
and the number is read from the local data table using QFETCH().
\note This macro can only be used in test methods of a class with an
\c initTestCase_data() method.
*/
/*! \macro QWARN(message) /*! \macro QWARN(message)
\relates QTest \relates QTest
@ -255,7 +282,7 @@
This macro can be used to force a test failure. The test stops This macro can be used to force a test failure. The test stops
executing and the failure \a message is appended to the test log. executing and the failure \a message is appended to the test log.
\b {Note:} This macro can only be used in a test function that is invoked \note This macro can only be used in a test function that is invoked
by the test framework. by the test framework.
Example: Example:
@ -332,7 +359,7 @@
\a mode is a \l QTest::TestFailMode and sets whether the test should \a mode is a \l QTest::TestFailMode and sets whether the test should
continue to execute or not. continue to execute or not.
\b {Note:} This macro can only be used in a test function that is invoked \note This macro can only be used in a test function that is invoked
by the test framework. by the test framework.
Example 1: Example 1:
@ -394,13 +421,13 @@
test has been installed, and regardless of whether the test's build tree test has been installed, and regardless of whether the test's build tree
is equal to the test's source tree. is equal to the test's source tree.
\b {Note:} reliable detection of testdata from the source directory requires \note reliable detection of testdata from the source directory requires
either that qmake is used, or the \c{QT_TESTCASE_BUILDDIR} macro is defined to either that qmake is used, or the \c{QT_TESTCASE_BUILDDIR} macro is defined to
point to the working directory from which the compiler is invoked, or only point to the working directory from which the compiler is invoked, or only
absolute paths to the source files are passed to the compiler. Otherwise, the absolute paths to the source files are passed to the compiler. Otherwise, the
absolute path of the source directory cannot be determined. absolute path of the source directory cannot be determined.
\b {Note:} For tests that use the \l QTEST_APPLESS_MAIN() macro to generate a \note For tests that use the \l QTEST_APPLESS_MAIN() macro to generate a
\c{main()} function, \c{QFINDTESTDATA} will not attempt to find test data \c{main()} function, \c{QFINDTESTDATA} will not attempt to find test data
relative to QCoreApplication::applicationDirPath(). In practice, this means that relative to QCoreApplication::applicationDirPath(). In practice, this means that
tests using \c{QTEST_APPLESS_MAIN()} will fail to find their test data tests using \c{QTEST_APPLESS_MAIN()} will fail to find their test data
@ -422,7 +449,7 @@
Similarly, if qmake is used and the configuration includes \c{QT += gui}, then Similarly, if qmake is used and the configuration includes \c{QT += gui}, then
\c QT_GUI_LIB will be defined automatically. \c QT_GUI_LIB will be defined automatically.
\b {Note:} On platforms that have keypad navigation enabled by default, \note On platforms that have keypad navigation enabled by default,
this macro will forcefully disable it if \c QT_WIDGETS_LIB is defined. This is done this macro will forcefully disable it if \c QT_WIDGETS_LIB is defined. This is done
to simplify the usage of key events when writing autotests. If you wish to write a to simplify the usage of key events when writing autotests. If you wish to write a
test case that uses keypad navigation, you should enable it either in the test case that uses keypad navigation, you should enable it either in the
@ -662,7 +689,7 @@
Simulates pressing a \a key with an optional \a modifier on a \a widget. If \a delay Simulates pressing a \a key with an optional \a modifier on a \a widget. If \a delay
is larger than 0, the test will wait for \a delay milliseconds before pressing the key. is larger than 0, the test will wait for \a delay milliseconds before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease(). \note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick() \sa QTest::keyRelease(), QTest::keyClick()
*/ */
@ -674,7 +701,7 @@
If \a delay is larger than 0, the test will wait for \a delay milliseconds If \a delay is larger than 0, the test will wait for \a delay milliseconds
before pressing the key. before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease(). \note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick() \sa QTest::keyRelease(), QTest::keyClick()
*/ */
@ -686,7 +713,7 @@
Simulates pressing a \a key with an optional \a modifier on a \a window. If \a delay Simulates pressing a \a key with an optional \a modifier on a \a window. If \a delay
is larger than 0, the test will wait for \a delay milliseconds before pressing the key. is larger than 0, the test will wait for \a delay milliseconds before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease(). \note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick() \sa QTest::keyRelease(), QTest::keyClick()
*/ */
@ -699,7 +726,7 @@
If \a delay is larger than 0, the test will wait for \a delay milliseconds If \a delay is larger than 0, the test will wait for \a delay milliseconds
before pressing the key. before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease(). \note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick() \sa QTest::keyRelease(), QTest::keyClick()
*/ */
@ -920,12 +947,12 @@
You can add specializations or overloads of this function to your test to enable You can add specializations or overloads of this function to your test to enable
verbose output. verbose output.
\b {Note:} Starting with Qt 5.5, you should prefer to provide a toString() function \note Starting with Qt 5.5, you should prefer to provide a toString() function
in the type's namespace instead of specializing this template. in the type's namespace instead of specializing this template.
If your code needs to continue to work with the QTestLib from Qt 5.4 or If your code needs to continue to work with the QTestLib from Qt 5.4 or
earlier, you need to continue to use specialization. earlier, you need to continue to use specialization.
\b {Note:} The caller of toString() must delete the returned data \note The caller of toString() must delete the returned data
using \c{delete[]}. Your implementation should return a string using \c{delete[]}. Your implementation should return a string
created with \c{new[]} or qstrdup(). The easiest way to do so is to created with \c{new[]} or qstrdup(). The easiest way to do so is to
create a QByteArray or QString and calling QTest::toString() on it create a QByteArray or QString and calling QTest::toString() on it

View File

@ -63,8 +63,9 @@ SOURCES += \
../../corelib/kernel/qsharedmemory.cpp \ ../../corelib/kernel/qsharedmemory.cpp \
../../corelib/kernel/qsystemsemaphore.cpp \ ../../corelib/kernel/qsystemsemaphore.cpp \
../../corelib/plugin/quuid.cpp \ ../../corelib/plugin/quuid.cpp \
../../corelib/serialization/qcborvalue.cpp \
../../corelib/serialization/qdatastream.cpp \ ../../corelib/serialization/qdatastream.cpp \
../../corelib/serialization/qjson.cpp \ ../../corelib/serialization/qjsoncbor.cpp \
../../corelib/serialization/qjsondocument.cpp \ ../../corelib/serialization/qjsondocument.cpp \
../../corelib/serialization/qjsonobject.cpp \ ../../corelib/serialization/qjsonobject.cpp \
../../corelib/serialization/qjsonarray.cpp \ ../../corelib/serialization/qjsonarray.cpp \

View File

@ -1680,7 +1680,7 @@ static void writeResourceIcon(QTextStream &output,
"Selected", "Off"); "Selected", "Off");
} }
if (i->hasElementSelectedOn()) { if (i->hasElementSelectedOn()) {
writeIconAddFile(output, indent, iconName, i->elementSelectedOff()->text(), writeIconAddFile(output, indent, iconName, i->elementSelectedOn()->text(),
"Selected", "On"); "Selected", "On");
} }
} }

View File

@ -1107,10 +1107,10 @@ QMenu::indicator:exclusive:checked:selected {
QMenuBar { QMenuBar {
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 lightgray, stop:1 darkgray); stop:0 lightgray, stop:1 darkgray);
spacing: 3px; /* spacing between menu bar items */
} }
QMenuBar::item { QMenuBar::item {
spacing: 3px; /* spacing between menu bar items */
padding: 1px 4px; padding: 1px 4px;
background: transparent; background: transparent;
border-radius: 4px; border-radius: 4px;

View File

@ -549,6 +549,10 @@
If the property references an enum declared with Q_ENUMS, you should If the property references an enum declared with Q_ENUMS, you should
reference its constants by name, i.e., not their numeric value. reference its constants by name, i.e., not their numeric value.
\note Use the qproperty syntax with care, as it modifies the
widget that is being painted. Also, the qproperty syntax is evaluated only
once, which is when the widget is polished by the style. This means that any
attempt to use them in pseudo-states such as QPushButton:hover, will not work.
*/ */
/*! /*!

View File

@ -386,7 +386,15 @@ void QGraphicsScenePrivate::_q_emitUpdated()
// Notify the changes to anybody interested. // Notify the changes to anybody interested.
QList<QRectF> oldUpdatedRects; QList<QRectF> oldUpdatedRects;
oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects; if (updateAll) {
oldUpdatedRects << q->sceneRect();
} else {
// Switch to a ranged constructor in Qt 6...
oldUpdatedRects.reserve(int(updatedRects.size()));
std::copy(updatedRects.cbegin(), updatedRects.cend(),
std::back_inserter(oldUpdatedRects));
}
updateAll = false; updateAll = false;
updatedRects.clear(); updatedRects.clear();
emit q->changed(oldUpdatedRects); emit q->changed(oldUpdatedRects);
@ -3219,8 +3227,7 @@ void QGraphicsScene::update(const QRectF &rect)
view->d_func()->updateRectF(rect); view->d_func()->updateRectF(rect);
} }
} else { } else {
if (!d->updatedRects.contains(rect)) d->updatedRects.insert(rect);
d->updatedRects << rect;
} }
} }

View File

@ -69,6 +69,9 @@
#include <QtWidgets/qstyle.h> #include <QtWidgets/qstyle.h>
#include <QtWidgets/qstyleoption.h> #include <QtWidgets/qstyleoption.h>
#include <set>
#include <tuple>
QT_REQUIRE_CONFIG(graphicsview); QT_REQUIRE_CONFIG(graphicsview);
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -122,7 +125,19 @@ public:
QRectF growingItemsBoundingRect; QRectF growingItemsBoundingRect;
void _q_emitUpdated(); void _q_emitUpdated();
QList<QRectF> updatedRects;
struct UpdatedRectsCmp
{
bool operator() (const QRectF &a, const QRectF &b) const noexcept
{
return std::make_tuple(a.y(), a.x(), a.height(), a.width())
< std::make_tuple(b.y(), b.x(), b.height(), b.width());
}
};
// std::set was used here instead of std::unordered_set due to requiring only a comparator and
// showing equivalent performance in empirical measurements within the ranges of interest...
std::set<QRectF, UpdatedRectsCmp> updatedRects;
QPainterPath selectionArea; QPainterPath selectionArea;
int selectionChanging; int selectionChanging;

View File

@ -514,15 +514,6 @@ void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,
QStyle *style = widget ? widget->style() : QApplication::style(); QStyle *style = widget ? widget->style() : QApplication::style();
QRect geom = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget); QRect geom = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget);
const int delta = qSmartMinSize(editor).width() - geom.width();
if (delta > 0) {
//we need to widen the geometry
if (editor->layoutDirection() == Qt::RightToLeft)
geom.adjust(-delta, 0, 0, 0);
else
geom.adjust(0, 0, delta, 0);
}
editor->setGeometry(geom); editor->setGeometry(geom);
} }

View File

@ -3331,7 +3331,7 @@ void QTableViewPrivate::selectRow(int row, bool anchor)
if (q->selectionMode() != QTableView::SingleSelection if (q->selectionMode() != QTableView::SingleSelection
&& command.testFlag(QItemSelectionModel::Toggle)) { && command.testFlag(QItemSelectionModel::Toggle)) {
if (anchor) if (anchor)
ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows().contains(index) ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows(column).contains(index)
? QItemSelectionModel::Deselect : QItemSelectionModel::Select; ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
command &= ~QItemSelectionModel::Toggle; command &= ~QItemSelectionModel::Toggle;
command |= ctrlDragSelectionFlag; command |= ctrlDragSelectionFlag;

View File

@ -1868,22 +1868,19 @@ void QApplication::aboutQt()
bool QApplication::event(QEvent *e) bool QApplication::event(QEvent *e)
{ {
Q_D(QApplication); Q_D(QApplication);
if(e->type() == QEvent::Close) { if (e->type() == QEvent::Quit) {
QCloseEvent *ce = static_cast<QCloseEvent*>(e);
ce->accept();
closeAllWindows(); closeAllWindows();
for (auto *w : topLevelWidgets()) {
const QWidgetList list = topLevelWidgets();
for (auto *w : list) {
if (w->isVisible() && !(w->windowType() == Qt::Desktop) && !(w->windowType() == Qt::Popup) && if (w->isVisible() && !(w->windowType() == Qt::Desktop) && !(w->windowType() == Qt::Popup) &&
(!(w->windowType() == Qt::Dialog) || !w->parentWidget())) { (!(w->windowType() == Qt::Dialog) || !w->parentWidget())) {
ce->ignore(); e->ignore();
break; return true;
} }
} }
if (ce->isAccepted()) { // Explicitly call QCoreApplication instead of QGuiApplication so that
return true; // we don't let QGuiApplication close any windows we skipped earlier in
} // closeAllWindows(). FIXME: Unify all this close magic through closeAllWindows.
return QCoreApplication::event(e);
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
} else if (e->type() == QEvent::LocaleChange) { } else if (e->type() == QEvent::LocaleChange) {
// on Windows the event propagation is taken care by the // on Windows the event propagation is taken care by the

View File

@ -893,7 +893,7 @@ void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget
void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
{ {
auto *widget = findDnDTarget(m_widget, event->pos()); QPointer<QWidget> widget = findDnDTarget(m_widget, event->pos());
if (!widget) { if (!widget) {
event->ignore(); event->ignore();
if (m_dragTarget) { // Send DragLeave to previous if (m_dragTarget) { // Send DragLeave to previous
@ -916,14 +916,18 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event);
m_dragTarget = nullptr; m_dragTarget = nullptr;
} }
// Send DragEnter to new widget. // widget might have been deleted when handling the leaveEvent
handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget); if (widget) {
// Handling 'DragEnter' should suffice for the application. // Send DragEnter to new widget.
translated.setDropAction(event->dropAction()); handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget);
translated.setAccepted(event->isAccepted()); // Handling 'DragEnter' should suffice for the application.
// The drag enter event is always immediately followed by a drag move event, translated.setDropAction(event->dropAction());
// see QDragEnterEvent documentation. translated.setAccepted(event->isAccepted());
QGuiApplication::forwardEvent(m_dragTarget, &translated, event); // The drag enter event is always immediately followed by a drag move event,
// see QDragEnterEvent documentation.
if (m_dragTarget)
QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
}
} }
event->setAccepted(translated.isAccepted()); event->setAccepted(translated.isAccepted());
event->setDropAction(translated.dropAction()); event->setDropAction(translated.dropAction());

View File

@ -550,6 +550,10 @@ void tst_QFile::exists()
QFile unc(uncPath); QFile unc(uncPath);
QVERIFY2(unc.exists(), msgFileDoesNotExist(uncPath).constData()); QVERIFY2(unc.exists(), msgFileDoesNotExist(uncPath).constData());
#endif #endif
QTest::ignoreMessage(QtWarningMsg, "Broken filename passed to function");
QVERIFY(!QFile::exists(QDir::currentPath() + QLatin1Char('/') +
QChar(QChar::Null) + QLatin1String("x/y")));
} }
void tst_QFile::open_data() void tst_QFile::open_data()

Some files were not shown because too many files have changed in this diff Show More