Merge remote-tracking branch 'origin/5.15' into dev
Change-Id: I379794a01cbf6fb39d94b24cc8c90b1971a212b9
This commit is contained in:
commit
7b3bdcbfe8
@ -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
|
||||||
|
@ -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=
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 \
|
||||||
|
@ -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.
|
||||||
|
@ -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 \
|
||||||
|
@ -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" ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
415
src/corelib/serialization/qbinaryjson.cpp
Normal file
415
src/corelib/serialization/qbinaryjson.cpp
Normal 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
|
617
src/corelib/serialization/qbinaryjson_p.h
Normal file
617
src/corelib/serialization/qbinaryjson_p.h
Normal 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
|
137
src/corelib/serialization/qbinaryjsonarray.cpp
Normal file
137
src/corelib/serialization/qbinaryjsonarray.cpp
Normal 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
|
||||||
|
|
98
src/corelib/serialization/qbinaryjsonarray_p.h
Normal file
98
src/corelib/serialization/qbinaryjsonarray_p.h
Normal 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
|
149
src/corelib/serialization/qbinaryjsonobject.cpp
Normal file
149
src/corelib/serialization/qbinaryjsonobject.cpp
Normal 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
|
97
src/corelib/serialization/qbinaryjsonobject_p.h
Normal file
97
src/corelib/serialization/qbinaryjsonobject_p.h
Normal 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
|
155
src/corelib/serialization/qbinaryjsonvalue.cpp
Normal file
155
src/corelib/serialization/qbinaryjsonvalue.cpp
Normal 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
|
134
src/corelib/serialization/qbinaryjsonvalue_p.h
Normal file
134
src/corelib/serialization/qbinaryjsonvalue_p.h
Normal 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
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
<< ")";
|
<< ")";
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
<< ')';
|
<< ')';
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
<< ")";
|
<< ")";
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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, ' ');
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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()
|
||||||
|
@ -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);
|
||||||
|
@ -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") },
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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 ")
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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 \
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
@ -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
Loading…
Reference in New Issue
Block a user