Reimplement JSON support on top of Cbor

In turn, deprecate the QJsonDocument methods that deal with JSON binary
data. You should use CBOR for data serialization these days.

[ChangeLog][Deprecation Notice] The binary JSON representation is
deprecated. The CBOR format should be used instead.

Fixes: QTBUG-47629
Change-Id: Ic8b92ea36de87815b12307a9d8b1095f07166db8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ulf Hermann 2019-06-19 13:43:00 +02:00
parent 559b563d71
commit 35adb74ddd
32 changed files with 2843 additions and 2347 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,415 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qbinaryjson_p.h"
#include <qjsonobject.h>
#include <qjsonarray.h>
QT_BEGIN_NAMESPACE
namespace QBinaryJsonPrivate {
static Q_CONSTEXPR Base emptyArray = {
{ qle_uint(sizeof(Base)) },
{ 0 },
{ qle_uint(0) }
};
static Q_CONSTEXPR Base emptyObject = {
{ qle_uint(sizeof(Base)) },
{ qToLittleEndian(1U) },
{ qle_uint(0) }
};
void MutableData::compact()
{
Q_STATIC_ASSERT(sizeof(Value) == sizeof(offset));
Base *base = header->root();
int reserve = 0;
if (base->is_object) {
auto *o = static_cast<Object *>(base);
for (uint i = 0; i < o->length; ++i)
reserve += o->entryAt(i)->usedStorage(o);
} else {
auto *a = static_cast<Array *>(base);
for (uint i = 0; i < a->length; ++i)
reserve += a->at(i)->usedStorage(a);
}
uint size = sizeof(Base) + reserve + base->length * sizeof(offset);
uint alloc = sizeof(Header) + size;
auto *h = reinterpret_cast<Header *>(malloc(alloc));
Q_CHECK_PTR(h);
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1;
Base *b = h->root();
b->size = size;
b->is_object = header->root()->is_object;
b->length = base->length;
b->tableOffset = reserve + sizeof(Array);
uint offset = sizeof(Base);
if (b->is_object) {
const auto *o = static_cast<const Object *>(base);
auto *no = static_cast<Object *>(b);
for (uint i = 0; i < o->length; ++i) {
no->table()[i] = offset;
const Entry *e = o->entryAt(i);
Entry *ne = no->entryAt(i);
uint s = e->size();
memcpy(ne, e, s);
offset += s;
uint dataSize = e->value.usedStorage(o);
if (dataSize) {
memcpy(reinterpret_cast<char *>(no) + offset, e->value.data(o), dataSize);
ne->value.value = offset;
offset += dataSize;
}
}
} else {
const auto *a = static_cast<const Array *>(base);
auto *na = static_cast<Array *>(b);
for (uint i = 0; i < a->length; ++i) {
const Value *v = a->at(i);
Value *nv = na->at(i);
*nv = *v;
uint dataSize = v->usedStorage(a);
if (dataSize) {
memcpy(reinterpret_cast<char *>(na) + offset, v->data(a), dataSize);
nv->value = offset;
offset += dataSize;
}
}
}
Q_ASSERT(offset == uint(b->tableOffset));
free(header);
header = h;
this->alloc = alloc;
compactionCounter = 0;
}
bool ConstData::isValid() const
{
if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1U)
return false;
const Base *root = header->root();
const uint maxSize = alloc - sizeof(Header);
return root->is_object
? static_cast<const Object *>(root)->isValid(maxSize)
: static_cast<const Array *>(root)->isValid(maxSize);
}
QJsonDocument ConstData::toJsonDocument() const
{
const Base *root = header->root();
return root->is_object
? QJsonDocument(static_cast<const Object *>(root)->toJsonObject())
: QJsonDocument(static_cast<const Array *>(root)->toJsonArray());
}
uint Base::reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace)
{
Q_ASSERT(posInTable <= length);
if (size + dataSize >= Value::MaxSize) {
qWarning("QJson: Document too large to store in data structure %d %d %d",
uint(size), dataSize, Value::MaxSize);
return 0;
}
offset off = tableOffset;
// move table to new position
if (replace) {
memmove(reinterpret_cast<char *>(table()) + dataSize, table(), length * sizeof(offset));
} else {
memmove(reinterpret_cast<char *>(table() + posInTable + numItems) + dataSize,
table() + posInTable, (length - posInTable) * sizeof(offset));
memmove(reinterpret_cast<char *>(table()) + dataSize, table(), posInTable * sizeof(offset));
}
tableOffset += dataSize;
for (uint i = 0; i < numItems; ++i)
table()[posInTable + i] = off;
size += dataSize;
if (!replace) {
length += numItems;
size += numItems * sizeof(offset);
}
return off;
}
uint Object::indexOf(QStringView key, bool *exists) const
{
uint min = 0;
uint n = length;
while (n > 0) {
uint half = n >> 1;
uint middle = min + half;
if (*entryAt(middle) >= key) {
n = half;
} else {
min = middle + 1;
n -= half + 1;
}
}
if (min < length && *entryAt(min) == key) {
*exists = true;
return min;
}
*exists = false;
return min;
}
QJsonObject Object::toJsonObject() const
{
QJsonObject object;
for (uint i = 0; i < length; ++i) {
const Entry *e = entryAt(i);
object.insert(e->key(), e->value.toJsonValue(this));
}
return object;
}
bool Object::isValid(uint maxSize) const
{
if (size > maxSize || tableOffset + length * sizeof(offset) > size)
return false;
QString lastKey;
for (uint i = 0; i < length; ++i) {
if (table()[i] + sizeof(Entry) >= tableOffset)
return false;
const Entry *e = entryAt(i);
if (!e->isValid(tableOffset - table()[i]))
return false;
const QString key = e->key();
if (key < lastKey)
return false;
if (!e->value.isValid(this))
return false;
lastKey = key;
}
return true;
}
QJsonArray Array::toJsonArray() const
{
QJsonArray array;
const offset *values = table();
for (uint i = 0; i < length; ++i)
array.append(reinterpret_cast<const Value *>(values + i)->toJsonValue(this));
return array;
}
bool Array::isValid(uint maxSize) const
{
if (size > maxSize || tableOffset + length * sizeof(offset) > size)
return false;
const offset *values = table();
for (uint i = 0; i < length; ++i) {
if (!reinterpret_cast<const Value *>(values + i)->isValid(this))
return false;
}
return true;
}
uint Value::usedStorage(const Base *b) const
{
uint s = 0;
switch (type) {
case QJsonValue::Double:
if (!latinOrIntValue)
s = sizeof(double);
break;
case QJsonValue::String: {
const char *d = data(b);
s = latinOrIntValue
? (sizeof(ushort)
+ qFromLittleEndian(*reinterpret_cast<const ushort *>(d)))
: (sizeof(int)
+ sizeof(ushort) * qFromLittleEndian(*reinterpret_cast<const int *>(d)));
break;
}
case QJsonValue::Array:
case QJsonValue::Object:
s = base(b)->size;
break;
case QJsonValue::Null:
case QJsonValue::Bool:
default:
break;
}
return alignedSize(s);
}
QJsonValue Value::toJsonValue(const Base *b) const
{
switch (type) {
case QJsonValue::Null:
return QJsonValue(QJsonValue::Null);
case QJsonValue::Bool:
return QJsonValue(toBoolean());
case QJsonValue::Double:
return QJsonValue(toDouble(b));
case QJsonValue::String:
return QJsonValue(toString(b));
case QJsonValue::Array:
return static_cast<const Array *>(base(b))->toJsonArray();
case QJsonValue::Object:
return static_cast<const Object *>(base(b))->toJsonObject();
case QJsonValue::Undefined:
return QJsonValue(QJsonValue::Undefined);
}
Q_UNREACHABLE();
return QJsonValue(QJsonValue::Undefined);
}
inline bool isValidValueOffset(uint offset, uint tableOffset)
{
return offset >= sizeof(Base)
&& offset + sizeof(uint) <= tableOffset;
}
bool Value::isValid(const Base *b) const
{
switch (type) {
case QJsonValue::Null:
case QJsonValue::Bool:
return true;
case QJsonValue::Double:
return latinOrIntValue || isValidValueOffset(value, b->tableOffset);
case QJsonValue::String:
if (!isValidValueOffset(value, b->tableOffset))
return false;
if (latinOrIntValue)
return asLatin1String(b).isValid(b->tableOffset - value);
return asString(b).isValid(b->tableOffset - value);
case QJsonValue::Array:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<const Array *>(base(b))->isValid(b->tableOffset - value);
case QJsonValue::Object:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<const Object *>(base(b))->isValid(b->tableOffset - value);
default:
return false;
}
}
uint Value::requiredStorage(const QBinaryJsonValue &v, bool *compressed)
{
*compressed = false;
switch (v.type()) {
case QJsonValue::Double:
if (QBinaryJsonPrivate::compressedNumber(v.toDouble()) != INT_MAX) {
*compressed = true;
return 0;
}
return sizeof(double);
case QJsonValue::String: {
QString s = v.toString();
*compressed = QBinaryJsonPrivate::useCompressed(s);
return QBinaryJsonPrivate::qStringSize(s, *compressed);
}
case QJsonValue::Array:
case QJsonValue::Object:
return v.base ? uint(v.base->size) : sizeof(QBinaryJsonPrivate::Base);
case QJsonValue::Undefined:
case QJsonValue::Null:
case QJsonValue::Bool:
break;
}
return 0;
}
uint Value::valueToStore(const QBinaryJsonValue &v, uint offset)
{
switch (v.type()) {
case QJsonValue::Undefined:
case QJsonValue::Null:
break;
case QJsonValue::Bool:
return v.toBool();
case QJsonValue::Double: {
int c = QBinaryJsonPrivate::compressedNumber(v.toDouble());
if (c != INT_MAX)
return c;
}
Q_FALLTHROUGH();
case QJsonValue::String:
case QJsonValue::Array:
case QJsonValue::Object:
return offset;
}
return 0;
}
void Value::copyData(const QBinaryJsonValue &v, char *dest, bool compressed)
{
switch (v.type()) {
case QJsonValue::Double:
if (!compressed)
qToLittleEndian(v.toDouble(), dest);
break;
case QJsonValue::String: {
const QString str = v.toString();
QBinaryJsonPrivate::copyString(dest, str, compressed);
break;
}
case QJsonValue::Array:
case QJsonValue::Object: {
const QBinaryJsonPrivate::Base *b = v.base;
if (!b)
b = (v.type() == QJsonValue::Array ? &emptyArray : &emptyObject);
memcpy(dest, b, b->size);
break;
}
default:
break;
}
}
} // namespace QBinaryJsonPrivate
QT_END_NAMESPACE

View File

@ -0,0 +1,617 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QBINARYJSON_P_H
#define QBINARYJSON_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qbinaryjsonvalue_p.h>
#include <private/qendian_p.h>
#include <qjsondocument.h>
#include <limits>
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
// in qstring.cpp
void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len);
void qt_from_latin1(ushort *dst, const char *str, size_t size) noexcept;
/*
This defines a binary data structure for Json data. The data structure is optimised for fast reading
and minimum allocations. The whole data structure can be mmap'ed and used directly.
In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but
much faster to access.
The size requirements are:
String:
Latin1 data: 2 bytes header + string.length()
Full Unicode: 4 bytes header + 2*(string.length())
Values: 4 bytes + size of data (size can be 0 for some data)
bool: 0 bytes
double: 8 bytes (0 if integer with less than 27bits)
string: see above
array: size of array
object: size of object
Array: 12 bytes + 4*length + size of Value data
Object: 12 bytes + 8*length + size of Key Strings + size of Value data
For an example such as
{ // object: 12 + 5*8 = 52
"firstName": "John", // key 12, value 8 = 20
"lastName" : "Smith", // key 12, value 8 = 20
"age" : 25, // key 8, value 0 = 8
"address" : // key 12, object below = 140
{ // object: 12 + 4*8
"streetAddress": "21 2nd Street", // key 16, value 16
"city" : "New York", // key 8, value 12
"state" : "NY", // key 8, value 4
"postalCode" : "10021" // key 12, value 8
}, // object total: 128
"phoneNumber": // key: 16, value array below = 172
[ // array: 12 + 2*4 + values below: 156
{ // object 12 + 2*8
"type" : "home", // key 8, value 8
"number": "212 555-1234" // key 8, value 16
}, // object total: 68
{ // object 12 + 2*8
"type" : "fax", // key 8, value 8
"number": "646 555-4567" // key 8, value 16
} // object total: 68
] // array total: 156
} // great total: 412 bytes
The uncompressed text file used roughly 500 bytes, so in this case we end up using about
the same space as the text representation.
Other measurements have shown a slightly bigger binary size than a compact text
representation where all possible whitespace was stripped out.
*/
namespace QBinaryJsonPrivate {
class Array;
class Object;
class Value;
class Entry;
template<typename T>
using q_littleendian = QLEInteger<T>;
using qle_short = q_littleendian<short>;
using qle_ushort = q_littleendian<unsigned short>;
using qle_int = q_littleendian<int>;
using qle_uint = q_littleendian<unsigned int>;
template<int pos, int width>
using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
template<int pos, int width>
using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
using offset = qle_uint;
// round the size up to the next 4 byte boundary
inline uint alignedSize(uint size) { return (size + 3) & ~3; }
const int MaxLatin1Length = 0x7fff;
static inline bool useCompressed(QStringView s)
{
if (s.length() > MaxLatin1Length)
return false;
return QtPrivate::isLatin1(s);
}
static inline bool useCompressed(QLatin1String s)
{
return s.size() <= MaxLatin1Length;
}
static inline uint qStringSize(const QString &string, bool compress)
{
uint l = 2 + string.size();
if (!compress)
l *= 2;
return alignedSize(l);
}
// returns INT_MAX if it can't compress it into 28 bits
static inline int compressedNumber(double d)
{
// this relies on details of how ieee floats are represented
const int exponent_off = 52;
const quint64 fraction_mask = 0x000fffffffffffffULL;
const quint64 exponent_mask = 0x7ff0000000000000ULL;
quint64 val;
memcpy (&val, &d, sizeof(double));
int exp = (int)((val & exponent_mask) >> exponent_off) - 1023;
if (exp < 0 || exp > 25)
return std::numeric_limits<int>::max();
quint64 non_int = val & (fraction_mask >> exp);
if (non_int)
return std::numeric_limits<int>::max();
bool neg = (val >> 63) != 0;
val &= fraction_mask;
val |= ((quint64)1 << 52);
int res = (int)(val >> (52 - exp));
return neg ? -res : res;
}
class Latin1String;
class String
{
public:
explicit String(const char *data) : d(reinterpret_cast<const Data *>(data)) {}
struct Data {
qle_uint length;
qle_ushort utf16[1];
};
const Data *d;
uint byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; }
bool isValid(uint maxSize) const
{
// Check byteSize() <= maxSize, avoiding integer overflow
return maxSize >= sizeof(uint)
&& uint(d->length) <= (maxSize - sizeof(uint)) / sizeof(ushort);
}
static void copy(char *dest, QStringView str)
{
Data *data = reinterpret_cast<Data *>(dest);
data->length = str.length();
qToLittleEndian<quint16>(str.utf16(), str.length(), data->utf16);
fillTrailingZeros(data);
}
static void fillTrailingZeros(Data *data)
{
if (data->length & 1)
data->utf16[data->length] = 0;
}
bool operator ==(QStringView str) const
{
int slen = str.length();
int l = d->length;
if (slen != l)
return false;
const auto *s = reinterpret_cast<const ushort *>(str.utf16());
const qle_ushort *a = d->utf16;
const ushort *b = s;
while (l-- && *a == *b)
a++,b++;
return (l == -1);
}
bool operator ==(const String &str) const
{
if (d->length != str.d->length)
return false;
return !memcmp(d->utf16, str.d->utf16, d->length * sizeof(ushort));
}
QString toString() const
{
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
return QString(reinterpret_cast<const QChar *>(d->utf16), d->length);
#else
const uint l = d->length;
QString str(l, Qt::Uninitialized);
QChar *ch = str.data();
for (uint i = 0; i < l; ++i)
ch[i] = QChar(d->utf16[i]);
return str;
#endif
}
};
class Latin1String
{
public:
explicit Latin1String(const char *data) : d(reinterpret_cast<const Data *>(data)) {}
struct Data {
qle_ushort length;
char latin1[1];
};
const Data *d;
uint byteSize() const { return sizeof(ushort) + sizeof(char) * (d->length); }
bool isValid(uint maxSize) const { return byteSize() <= maxSize; }
static void copy(char *dest, QStringView src)
{
Data *data = reinterpret_cast<Data *>(dest);
data->length = src.length();
auto *l = reinterpret_cast<uchar *>(data->latin1);
const auto *uc = reinterpret_cast<const ushort *>(src.utf16());
qt_to_latin1_unchecked(l, uc, data->length);
for (uint len = data->length; quintptr(l + len) & 0x3; ++len)
l[len] = 0;
}
QLatin1String toQLatin1String() const noexcept { return QLatin1String(d->latin1, d->length); }
QString toString() const { return QString::fromLatin1(d->latin1, d->length); }
};
static inline void copyString(char *dest, QStringView str, bool compress)
{
if (compress)
Latin1String::copy(dest, str);
else
String::copy(dest, str);
}
/*
Base is the base class for both Object and Array. Both classes work more or less the same way.
The class starts with a header (defined by the struct below), then followed by data (the data for
values in the Array case and Entry's (see below) for objects.
After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and
offsets from the beginning of the object to Entry's in the case of Object.
Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage
of a binary search over the keys in an Object.
*/
class Base
{
public:
qle_uint size;
union {
uint _dummy;
qle_bitfield<0, 1> is_object;
qle_bitfield<1, 31> length;
};
offset tableOffset;
// content follows here
bool isObject() const { return !!is_object; }
bool isArray() const { return !isObject(); }
offset *table()
{
return reinterpret_cast<offset *>(reinterpret_cast<char *>(this) + tableOffset);
}
const offset *table() const
{
return reinterpret_cast<const offset *>(reinterpret_cast<const char *>(this) + tableOffset);
}
uint reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace);
};
class Object : public Base
{
public:
const Entry *entryAt(uint i) const
{
return reinterpret_cast<const Entry *>(reinterpret_cast<const char *>(this) + table()[i]);
}
Entry *entryAt(uint i)
{
return reinterpret_cast<Entry *>(reinterpret_cast<char *>(this) + table()[i]);
}
uint indexOf(QStringView key, bool *exists) const;
QJsonObject toJsonObject() const;
bool isValid(uint maxSize) const;
};
class Array : public Base
{
public:
const Value *at(uint i) const { return reinterpret_cast<const Value *>(table() + i); }
Value *at(uint i) { return reinterpret_cast<Value *>(table() + i); }
QJsonArray toJsonArray() const;
bool isValid(uint maxSize) const;
};
class Value
{
public:
enum {
MaxSize = (1 << 27) - 1
};
union {
uint _dummy;
qle_bitfield<0, 3> type;
qle_bitfield<3, 1> latinOrIntValue;
qle_bitfield<4, 1> latinKey;
qle_bitfield<5, 27> value;
qle_signedbitfield<5, 27> int_value;
};
inline const char *data(const Base *b) const
{
return reinterpret_cast<const char *>(b) + value;
}
uint usedStorage(const Base *b) const;
bool toBoolean() const
{
Q_ASSERT(type == QJsonValue::Bool);
return value != 0;
}
double toDouble(const Base *b) const
{
Q_ASSERT(type == QJsonValue::Double);
if (latinOrIntValue)
return int_value;
auto i = qFromLittleEndian<quint64>(reinterpret_cast<const uchar *>(b) + value);
double d;
memcpy(&d, &i, sizeof(double));
return d;
}
QString toString(const Base *b) const
{
return latinOrIntValue
? asLatin1String(b).toString()
: asString(b).toString();
}
String asString(const Base *b) const
{
Q_ASSERT(type == QJsonValue::String && !latinOrIntValue);
return String(data(b));
}
Latin1String asLatin1String(const Base *b) const
{
Q_ASSERT(type == QJsonValue::String && latinOrIntValue);
return Latin1String(data(b));
}
const Base *base(const Base *b) const
{
Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object);
return reinterpret_cast<const Base *>(data(b));
}
QJsonValue toJsonValue(const Base *b) const;
bool isValid(const Base *b) const;
static uint requiredStorage(const QBinaryJsonValue &v, bool *compressed);
static uint valueToStore(const QBinaryJsonValue &v, uint offset);
static void copyData(const QBinaryJsonValue &v, char *dest, bool compressed);
};
class Entry {
public:
Value value;
// key
// value data follows key
uint size() const
{
uint s = sizeof(Entry);
if (value.latinKey)
s += shallowLatin1Key().byteSize();
else
s += shallowKey().byteSize();
return alignedSize(s);
}
uint usedStorage(Base *b) const
{
return size() + value.usedStorage(b);
}
String shallowKey() const
{
Q_ASSERT(!value.latinKey);
return String(reinterpret_cast<const char *>(this) + sizeof(Entry));
}
Latin1String shallowLatin1Key() const
{
Q_ASSERT(value.latinKey);
return Latin1String(reinterpret_cast<const char *>(this) + sizeof(Entry));
}
QString key() const
{
return value.latinKey
? shallowLatin1Key().toString()
: shallowKey().toString();
}
bool isValid(uint maxSize) const
{
if (maxSize < sizeof(Entry))
return false;
maxSize -= sizeof(Entry);
return value.latinKey
? shallowLatin1Key().isValid(maxSize)
: shallowKey().isValid(maxSize);
}
bool operator ==(QStringView key) const
{
return value.latinKey
? (shallowLatin1Key().toQLatin1String() == key)
: (shallowKey() == key);
}
bool operator >=(QStringView key) const
{
return value.latinKey
? (shallowLatin1Key().toQLatin1String() >= key)
: (shallowKey().toString() >= key);
}
};
class Header {
public:
qle_uint tag; // 'qbjs'
qle_uint version; // 1
Base *root() { return reinterpret_cast<Base *>(this + 1); }
const Base *root() const { return reinterpret_cast<const Base *>(this + 1); }
};
class ConstData
{
Q_DISABLE_COPY_MOVE(ConstData)
public:
const uint alloc;
union {
const char *rawData;
const Header *header;
};
ConstData(const char *raw, uint a) : alloc(a), rawData(raw) {}
bool isValid() const;
QJsonDocument toJsonDocument() const;
};
class MutableData
{
Q_DISABLE_COPY_MOVE(MutableData)
public:
QAtomicInt ref;
uint alloc;
union {
char *rawData;
Header *header;
};
uint compactionCounter : 31;
MutableData(char *raw, uint a)
: alloc(a), rawData(raw), compactionCounter(0)
{
}
MutableData(uint reserved, QJsonValue::Type valueType)
: rawData(nullptr), compactionCounter(0)
{
Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object);
alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset);
header = reinterpret_cast<Header *>(malloc(alloc));
Q_CHECK_PTR(header);
header->tag = QJsonDocument::BinaryFormatTag;
header->version = 1;
Base *b = header->root();
b->size = sizeof(Base);
b->is_object = (valueType == QJsonValue::Object);
b->tableOffset = sizeof(Base);
b->length = 0;
}
~MutableData()
{
free(rawData);
}
MutableData *clone(const Base *b, uint reserve = 0)
{
uint size = sizeof(Header) + b->size;
if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve)
return this;
if (reserve) {
if (reserve < 128)
reserve = 128;
size = qMax(size + reserve, qMin(size *2, uint(Value::MaxSize)));
if (size > Value::MaxSize) {
qWarning("QJson: Document too large to store in data structure");
return nullptr;
}
}
char *raw = reinterpret_cast<char *>(malloc(size));
Q_CHECK_PTR(raw);
memcpy(raw + sizeof(Header), b, b->size);
auto *h = reinterpret_cast<Header *>(raw);
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1;
auto *d = new MutableData(raw, size);
d->compactionCounter = (b == header->root()) ? compactionCounter : 0;
return d;
}
char *takeRawData(uint *size)
{
*size = alloc;
char *result = rawData;
rawData = nullptr;
alloc = 0;
return result;
}
void compact();
};
} // namespace QBinaryJsonPrivate
Q_DECLARE_TYPEINFO(QBinaryJsonPrivate::Value, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
#endif // QBINARYJSON_P_H

View File

@ -0,0 +1,137 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qbinaryjsonarray_p.h"
#include "qbinaryjson_p.h"
#include <qjsonarray.h>
QT_BEGIN_NAMESPACE
QBinaryJsonArray::~QBinaryJsonArray()
{
if (d && !d->ref.deref())
delete d;
}
QBinaryJsonArray QBinaryJsonArray::fromJsonArray(const QJsonArray &array)
{
QBinaryJsonArray binary;
for (const QJsonValue &value : array)
binary.append(QBinaryJsonValue::fromJsonValue(value));
if (binary.d) // We want to compact it as it is a root item now
binary.d->compactionCounter++;
binary.compact();
return binary;
}
void QBinaryJsonArray::append(const QBinaryJsonValue &value)
{
const uint i = a ? a->length : 0;
bool compressed;
uint valueSize = QBinaryJsonPrivate::Value::requiredStorage(value, &compressed);
if (!detach(valueSize + sizeof(QBinaryJsonPrivate::Value)))
return;
if (!a->length)
a->tableOffset = sizeof(QBinaryJsonPrivate::Array);
uint valueOffset = a->reserveSpace(valueSize, i, 1, false);
if (!valueOffset)
return;
QBinaryJsonPrivate::Value *v = a->at(i);
v->type = (value.t == QJsonValue::Undefined ? QJsonValue::Null : value.t);
v->latinOrIntValue = compressed;
v->latinKey = false;
v->value = QBinaryJsonPrivate::Value::valueToStore(value, valueOffset);
if (valueSize) {
QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast<char *>(a) + valueOffset,
compressed);
}
}
char *QBinaryJsonArray::takeRawData(uint *size)
{
if (d)
return d->takeRawData(size);
*size = 0;
return nullptr;
}
bool QBinaryJsonArray::detach(uint reserve)
{
if (!d) {
if (reserve >= QBinaryJsonPrivate::Value::MaxSize) {
qWarning("QBinaryJson: Document too large to store in data structure");
return false;
}
d = new QBinaryJsonPrivate::MutableData(reserve, QJsonValue::Array);
a = static_cast<QBinaryJsonPrivate::Array *>(d->header->root());
d->ref.ref();
return true;
}
if (reserve == 0 && d->ref.loadRelaxed() == 1)
return true;
QBinaryJsonPrivate::MutableData *x = d->clone(a, reserve);
if (!x)
return false;
x->ref.ref();
if (!d->ref.deref())
delete d;
d = x;
a = static_cast<QBinaryJsonPrivate::Array *>(d->header->root());
return true;
}
void QBinaryJsonArray::compact()
{
if (!d || !d->compactionCounter)
return;
detach();
d->compact();
a = static_cast<QBinaryJsonPrivate::Array *>(d->header->root());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QBINARYJSONARRAY_P_H
#define QBINARYJSONARRAY_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qbinaryjsonvalue_p.h"
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
class QBinaryJsonArray
{
Q_DISABLE_COPY(QBinaryJsonArray)
public:
QBinaryJsonArray() = default;
~QBinaryJsonArray();
QBinaryJsonArray(QBinaryJsonArray &&other) noexcept
: d(other.d),
a(other.a)
{
other.d = nullptr;
other.a = nullptr;
}
QBinaryJsonArray &operator =(QBinaryJsonArray &&other) noexcept
{
qSwap(d, other.d);
qSwap(a, other.a);
return *this;
}
static QBinaryJsonArray fromJsonArray(const QJsonArray &array);
char *takeRawData(uint *size);
private:
friend class QBinaryJsonValue;
void append(const QBinaryJsonValue &value);
void compact();
bool detach(uint reserve = 0);
QBinaryJsonPrivate::MutableData *d = nullptr;
QBinaryJsonPrivate::Array *a = nullptr;
};
QT_END_NAMESPACE
#endif // QBINARYJSONARRAY_P_H

View File

@ -0,0 +1,149 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qbinaryjsonobject_p.h"
#include "qbinaryjson_p.h"
#include <qjsonobject.h>
QT_BEGIN_NAMESPACE
QBinaryJsonObject::~QBinaryJsonObject()
{
if (d && !d->ref.deref())
delete d;
}
QBinaryJsonObject QBinaryJsonObject::fromJsonObject(const QJsonObject &object)
{
QBinaryJsonObject binary;
for (auto it = object.begin(), end = object.end(); it != end; ++it)
binary.insert(it.key(), QBinaryJsonValue::fromJsonValue(it.value()));
if (binary.d) // We want to compact it as it is a root item now
binary.d->compactionCounter++;
binary.compact();
return binary;
}
void QBinaryJsonObject::insert(const QString &key, const QBinaryJsonValue &value)
{
bool latinOrIntValue;
uint valueSize = QBinaryJsonPrivate::Value::requiredStorage(value, &latinOrIntValue);
bool latinKey = QBinaryJsonPrivate::useCompressed(key);
uint valueOffset = sizeof(QBinaryJsonPrivate::Entry)
+ QBinaryJsonPrivate::qStringSize(key, latinKey);
uint requiredSize = valueOffset + valueSize;
if (!detach(requiredSize + sizeof(QBinaryJsonPrivate::offset))) // offset for the new index entry
return;
if (!o->length)
o->tableOffset = sizeof(QBinaryJsonPrivate::Object);
bool keyExists = false;
uint pos = o->indexOf(key, &keyExists);
if (keyExists)
++d->compactionCounter;
uint off = o->reserveSpace(requiredSize, pos, 1, keyExists);
if (!off)
return;
QBinaryJsonPrivate::Entry *e = o->entryAt(pos);
e->value.type = value.t;
e->value.latinKey = latinKey;
e->value.latinOrIntValue = latinOrIntValue;
e->value.value = QBinaryJsonPrivate::Value::valueToStore(
value, reinterpret_cast<char *>(e) - reinterpret_cast<char *>(o) + valueOffset);
QBinaryJsonPrivate::copyString(reinterpret_cast<char *>(e + 1), key, latinKey);
if (valueSize) {
QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast<char *>(e) + valueOffset,
latinOrIntValue);
}
if (d->compactionCounter > 32U && d->compactionCounter >= unsigned(o->length) / 2U)
compact();
}
char *QBinaryJsonObject::takeRawData(uint *size) const
{
if (d)
return d->takeRawData(size);
*size = 0;
return nullptr;
}
bool QBinaryJsonObject::detach(uint reserve)
{
if (!d) {
if (reserve >= QBinaryJsonPrivate::Value::MaxSize) {
qWarning("QBinaryJson: Document too large to store in data structure");
return false;
}
d = new QBinaryJsonPrivate::MutableData(reserve, QJsonValue::Object);
o = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
d->ref.ref();
return true;
}
if (reserve == 0 && d->ref.loadRelaxed() == 1)
return true;
QBinaryJsonPrivate::MutableData *x = d->clone(o, reserve);
if (!x)
return false;
x->ref.ref();
if (!d->ref.deref())
delete d;
d = x;
o = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
return true;
}
void QBinaryJsonObject::compact()
{
if (!d || !d->compactionCounter)
return;
detach();
d->compact();
o = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QBINARYJSONOBJECT_H
#define QBINARYJSONOBJECT_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qbinaryjsonvalue_p.h"
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
class QBinaryJsonObject
{
Q_DISABLE_COPY(QBinaryJsonObject)
public:
QBinaryJsonObject() = default;
~QBinaryJsonObject();
QBinaryJsonObject(QBinaryJsonObject &&other) noexcept
: d(other.d), o(other.o)
{
other.d = nullptr;
other.o = nullptr;
}
QBinaryJsonObject &operator =(QBinaryJsonObject &&other) noexcept
{
qSwap(d, other.d);
qSwap(o, other.o);
return *this;
}
static QBinaryJsonObject fromJsonObject(const QJsonObject &object);
char *takeRawData(uint *size) const;
private:
friend class QBinaryJsonValue;
void insert(const QString &key, const QBinaryJsonValue &value);
bool detach(uint reserve = 0);
void compact();
QBinaryJsonPrivate::MutableData *d = nullptr;
QBinaryJsonPrivate::Object *o = nullptr;
};
QT_END_NAMESPACE
#endif // QBINARYJSONOBJECT_P_H

View File

@ -0,0 +1,155 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qbinaryjsonobject_p.h"
#include "qbinaryjsonvalue_p.h"
#include "qbinaryjsonarray_p.h"
#include "qbinaryjson_p.h"
#include <qjsonarray.h>
#include <qjsonobject.h>
QT_BEGIN_NAMESPACE
QBinaryJsonValue::QBinaryJsonValue(QBinaryJsonPrivate::MutableData *data,
QBinaryJsonPrivate::Base *parent,
const QBinaryJsonPrivate::Value &v)
: t(QJsonValue::Type(uint(v.type)))
{
switch (t) {
case QJsonValue::Undefined:
case QJsonValue::Null:
dbl = 0;
break;
case QJsonValue::Bool:
b = v.toBoolean();
break;
case QJsonValue::Double:
dbl = v.toDouble(parent);
break;
case QJsonValue::String: {
QString s = v.toString(parent);
stringData = s.data_ptr();
stringData->ref.ref();
break;
}
case QJsonValue::Array:
case QJsonValue::Object:
d = data;
base = v.base(parent);
break;
}
if (d)
d->ref.ref();
}
QBinaryJsonValue::QBinaryJsonValue(QString string)
: stringData(*reinterpret_cast<QStringData **>(&string)), t(QJsonValue::String)
{
stringData->ref.ref();
}
QBinaryJsonValue::QBinaryJsonValue(const QBinaryJsonArray &a)
: base(a.a), d(a.d), t(QJsonValue::Array)
{
if (d)
d->ref.ref();
}
QBinaryJsonValue::QBinaryJsonValue(const QBinaryJsonObject &o)
: base(o.o), d(o.d), t(QJsonValue::Object)
{
if (d)
d->ref.ref();
}
QBinaryJsonValue::~QBinaryJsonValue()
{
if (t == QJsonValue::String && stringData && !stringData->ref.deref())
free(stringData);
if (d && !d->ref.deref())
delete d;
}
QBinaryJsonValue QBinaryJsonValue::fromJsonValue(const QJsonValue &json)
{
switch (json.type()) {
case QJsonValue::Bool:
return QBinaryJsonValue(json.toBool());
case QJsonValue::Double:
return QBinaryJsonValue(json.toDouble());
case QJsonValue::String:
return QBinaryJsonValue(json.toString());
case QJsonValue::Array:
return QBinaryJsonArray::fromJsonArray(json.toArray());
case QJsonValue::Object:
return QBinaryJsonObject::fromJsonObject(json.toObject());
case QJsonValue::Null:
return QBinaryJsonValue(QJsonValue::Null);
case QJsonValue::Undefined:
return QBinaryJsonValue(QJsonValue::Undefined);
}
Q_UNREACHABLE();
return QBinaryJsonValue(QJsonValue::Null);
}
QString QBinaryJsonValue::toString() const
{
if (t != QJsonValue::String)
return QString();
stringData->ref.ref(); // the constructor below doesn't add a ref.
QStringDataPtr holder = { stringData };
return QString(holder);
}
void QBinaryJsonValue::detach()
{
if (!d)
return;
QBinaryJsonPrivate::MutableData *x = d->clone(base);
x->ref.ref();
if (!d->ref.deref())
delete d;
d = x;
base = static_cast<QBinaryJsonPrivate::Object *>(d->header->root());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,134 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QBINARYJSONVALUE_P_H
#define QBINARYJSONVALUE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qjsonvalue.h>
QT_REQUIRE_CONFIG(binaryjson);
QT_BEGIN_NAMESPACE
class QBinaryJsonArray;
class QBinaryJsonObject;
namespace QBinaryJsonPrivate {
class ConstData;
class MutableData;
class Base;
class Value;
class Object;
class Array;
}
class Q_CORE_EXPORT QBinaryJsonValue
{
Q_DISABLE_COPY(QBinaryJsonValue)
public:
explicit QBinaryJsonValue(QJsonValue::Type type) : ui(0), t(type) {}
explicit QBinaryJsonValue(bool b) : b(b), t(QJsonValue::Bool) {}
explicit QBinaryJsonValue(double n) : dbl(n), t(QJsonValue::Double) {}
explicit QBinaryJsonValue(QString s);
QBinaryJsonValue(const QBinaryJsonArray &a);
QBinaryJsonValue(const QBinaryJsonObject &o);
~QBinaryJsonValue();
QBinaryJsonValue(QBinaryJsonValue &&other) noexcept
: ui(other.ui),
d(other.d),
t(other.t)
{
other.ui = 0;
other.d = nullptr;
other.t = QJsonValue::Null;
}
QBinaryJsonValue &operator =(QBinaryJsonValue &&other) noexcept
{
qSwap(ui, other.ui);
qSwap(d, other.d);
qSwap(t, other.t);
return *this;
}
static QBinaryJsonValue fromJsonValue(const QJsonValue &json);
QJsonValue::Type type() const { return t; }
bool toBool() const { return (t == QJsonValue::Bool) && b; }
double toDouble() const { return (t == QJsonValue::Double) ? dbl : 0; }
QString toString() const;
private:
friend class QBinaryJsonPrivate::Value;
friend class QBinaryJsonArray;
friend class QBinaryJsonObject;
QBinaryJsonValue(QBinaryJsonPrivate::MutableData *d, QBinaryJsonPrivate::Base *parent,
const QBinaryJsonPrivate::Value &v);
void detach();
union {
quint64 ui;
bool b;
double dbl;
QStringData *stringData;
const QBinaryJsonPrivate::Base *base;
};
QBinaryJsonPrivate::MutableData *d = nullptr; // needed for Objects and Arrays
QJsonValue::Type t = QJsonValue::Null;
};
QT_END_NAMESPACE
#endif // QBINARYJSONVALUE_P_H

View File

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

View File

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

View File

@ -1,451 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qjson_p.h"
#include <qalgorithms.h>
QT_BEGIN_NAMESPACE
namespace QJsonPrivate
{
static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } };
static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { qToLittleEndian(1u) }, { qle_uint(0) } };
void Data::compact()
{
Q_ASSERT(sizeof(Value) == sizeof(offset));
if (!compactionCounter)
return;
Base *base = header->root();
int reserve = 0;
if (base->is_object) {
Object *o = static_cast<Object *>(base);
for (int i = 0; i < (int)o->length; ++i)
reserve += o->entryAt(i)->usedStorage(o);
} else {
Array *a = static_cast<Array *>(base);
for (int i = 0; i < (int)a->length; ++i)
reserve += (*a)[i].usedStorage(a);
}
int size = sizeof(Base) + reserve + base->length*sizeof(offset);
int alloc = sizeof(Header) + size;
Header *h = (Header *) malloc(alloc);
Q_CHECK_PTR(h);
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1;
Base *b = h->root();
b->size = size;
b->is_object = header->root()->is_object;
b->length = base->length;
b->tableOffset = reserve + sizeof(Array);
int offset = sizeof(Base);
if (b->is_object) {
Object *o = static_cast<Object *>(base);
Object *no = static_cast<Object *>(b);
for (int i = 0; i < (int)o->length; ++i) {
no->table()[i] = offset;
const Entry *e = o->entryAt(i);
Entry *ne = no->entryAt(i);
int s = e->size();
memcpy(ne, e, s);
offset += s;
int dataSize = e->value.usedStorage(o);
if (dataSize) {
memcpy((char *)no + offset, e->value.data(o), dataSize);
ne->value.value = offset;
offset += dataSize;
}
}
} else {
Array *a = static_cast<Array *>(base);
Array *na = static_cast<Array *>(b);
for (int i = 0; i < (int)a->length; ++i) {
const Value &v = (*a)[i];
Value &nv = (*na)[i];
nv = v;
int dataSize = v.usedStorage(a);
if (dataSize) {
memcpy((char *)na + offset, v.data(a), dataSize);
nv.value = offset;
offset += dataSize;
}
}
}
Q_ASSERT(offset == (int)b->tableOffset);
free(header);
header = h;
this->alloc = alloc;
compactionCounter = 0;
}
bool Data::valid() const
{
if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1u)
return false;
bool res = false;
Base *root = header->root();
int maxSize = alloc - sizeof(Header);
if (root->is_object)
res = static_cast<Object *>(root)->isValid(maxSize);
else
res = static_cast<Array *>(root)->isValid(maxSize);
return res;
}
int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace)
{
Q_ASSERT(posInTable >= 0 && posInTable <= (int)length);
if (size + dataSize >= Value::MaxSize) {
qWarning("QJson: Document too large to store in data structure %d %d %d", (uint)size, dataSize, Value::MaxSize);
return 0;
}
offset off = tableOffset;
// move table to new position
if (replace) {
memmove((char *)(table()) + dataSize, table(), length*sizeof(offset));
} else {
memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset));
memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset));
}
tableOffset += dataSize;
for (int i = 0; i < (int)numItems; ++i)
table()[posInTable + i] = off;
size += dataSize;
if (!replace) {
length += numItems;
size += numItems * sizeof(offset);
}
return off;
}
void Base::removeItems(int pos, int numItems)
{
Q_ASSERT(pos >= 0 && pos <= (int)length);
if (pos + numItems < (int)length)
memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset));
length -= numItems;
}
int Object::indexOf(QStringView key, bool *exists) const
{
int min = 0;
int n = length;
while (n > 0) {
int half = n >> 1;
int middle = min + half;
if (*entryAt(middle) >= key) {
n = half;
} else {
min = middle + 1;
n -= half + 1;
}
}
if (min < (int)length && *entryAt(min) == key) {
*exists = true;
return min;
}
*exists = false;
return min;
}
int Object::indexOf(QLatin1String key, bool *exists) const
{
int min = 0;
int n = length;
while (n > 0) {
int half = n >> 1;
int middle = min + half;
if (*entryAt(middle) >= key) {
n = half;
} else {
min = middle + 1;
n -= half + 1;
}
}
if (min < (int)length && *entryAt(min) == key) {
*exists = true;
return min;
}
*exists = false;
return min;
}
bool Object::isValid(int maxSize) const
{
if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size)
return false;
QString lastKey;
for (uint i = 0; i < length; ++i) {
offset entryOffset = table()[i];
if (entryOffset + sizeof(Entry) >= tableOffset)
return false;
Entry *e = entryAt(i);
if (!e->isValid(tableOffset - table()[i]))
return false;
QString key = e->key();
if (key < lastKey)
return false;
if (!e->value.isValid(this))
return false;
lastKey = key;
}
return true;
}
bool Array::isValid(int maxSize) const
{
if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size)
return false;
for (uint i = 0; i < length; ++i) {
if (!at(i).isValid(this))
return false;
}
return true;
}
bool Entry::operator ==(QStringView key) const
{
if (value.latinKey)
return (shallowLatin1Key() == key);
else
return (shallowKey() == key);
}
bool Entry::operator==(QLatin1String key) const
{
if (value.latinKey)
return shallowLatin1Key() == key;
else
return shallowKey() == QString(key); // ### conversion to QString
}
bool Entry::operator ==(const Entry &other) const
{
if (value.latinKey) {
if (other.value.latinKey)
return shallowLatin1Key() == other.shallowLatin1Key();
return shallowLatin1Key() == other.shallowKey();
} else if (other.value.latinKey) {
return shallowKey() == other.shallowLatin1Key();
}
return shallowKey() == other.shallowKey();
}
bool Entry::operator >=(const Entry &other) const
{
if (value.latinKey) {
if (other.value.latinKey)
return shallowLatin1Key() >= other.shallowLatin1Key();
return shallowLatin1Key() >= other.shallowKey();
} else if (other.value.latinKey) {
return shallowKey() >= other.shallowLatin1Key();
}
return shallowKey() >= other.shallowKey();
}
int Value::usedStorage(const Base *b) const
{
int s = 0;
switch (type) {
case QJsonValue::Double:
if (latinOrIntValue)
break;
s = sizeof(double);
break;
case QJsonValue::String: {
char *d = data(b);
if (latinOrIntValue)
s = sizeof(ushort) + qFromLittleEndian(*(ushort *)d);
else
s = sizeof(int) + sizeof(ushort) * qFromLittleEndian(*(int *)d);
break;
}
case QJsonValue::Array:
case QJsonValue::Object:
s = base(b)->size;
break;
case QJsonValue::Null:
case QJsonValue::Bool:
default:
break;
}
return alignedSize(s);
}
inline bool isValidValueOffset(uint offset, uint tableOffset)
{
return offset >= sizeof(Base)
&& offset + sizeof(uint) <= tableOffset;
}
bool Value::isValid(const Base *b) const
{
switch (type) {
case QJsonValue::Null:
case QJsonValue::Bool:
return true;
case QJsonValue::Double:
return latinOrIntValue || isValidValueOffset(value, b->tableOffset);
case QJsonValue::String:
if (!isValidValueOffset(value, b->tableOffset))
return false;
if (latinOrIntValue)
return asLatin1String(b).isValid(b->tableOffset - value);
return asString(b).isValid(b->tableOffset - value);
case QJsonValue::Array:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<Array *>(base(b))->isValid(b->tableOffset - value);
case QJsonValue::Object:
return isValidValueOffset(value, b->tableOffset)
&& static_cast<Object *>(base(b))->isValid(b->tableOffset - value);
default:
return false;
}
}
/*!
\internal
*/
int Value::requiredStorage(QJsonValue &v, bool *compressed)
{
*compressed = false;
switch (v.t) {
case QJsonValue::Double:
if (QJsonPrivate::compressedNumber(v.dbl) != INT_MAX) {
*compressed = true;
return 0;
}
return sizeof(double);
case QJsonValue::String: {
QString s = v.toString();
*compressed = QJsonPrivate::useCompressed(s);
return QJsonPrivate::qStringSize(s, *compressed);
}
case QJsonValue::Array:
case QJsonValue::Object:
if (v.d && v.d->compactionCounter) {
v.detach();
v.d->compact();
v.base = static_cast<QJsonPrivate::Base *>(v.d->header->root());
}
return v.base ? uint(v.base->size) : sizeof(QJsonPrivate::Base);
case QJsonValue::Undefined:
case QJsonValue::Null:
case QJsonValue::Bool:
break;
}
return 0;
}
/*!
\internal
*/
uint Value::valueToStore(const QJsonValue &v, uint offset)
{
switch (v.t) {
case QJsonValue::Undefined:
case QJsonValue::Null:
break;
case QJsonValue::Bool:
return v.b;
case QJsonValue::Double: {
int c = QJsonPrivate::compressedNumber(v.dbl);
if (c != INT_MAX)
return c;
}
Q_FALLTHROUGH();
case QJsonValue::String:
case QJsonValue::Array:
case QJsonValue::Object:
return offset;
}
return 0;
}
/*!
\internal
*/
void Value::copyData(const QJsonValue &v, char *dest, bool compressed)
{
switch (v.t) {
case QJsonValue::Double:
if (!compressed) {
qToLittleEndian(v.ui, dest);
}
break;
case QJsonValue::String: {
QString str = v.toString();
QJsonPrivate::copyString(dest, str, compressed);
break;
}
case QJsonValue::Array:
case QJsonValue::Object: {
const QJsonPrivate::Base *b = v.base;
if (!b)
b = (v.t == QJsonValue::Array ? &emptyArray : &emptyObject);
memcpy(dest, b, b->size);
break;
}
default:
break;
}
}
} // namespace QJsonPrivate
QT_END_NAMESPACE

View File

@ -52,743 +52,170 @@
// We mean it. // We mean it.
// //
#include <qjsonobject.h>
#include <qjsonvalue.h> #include <qjsonvalue.h>
#include <qjsondocument.h> #include <qcborvalue.h>
#include <qjsonarray.h> #include <private/qcborvalue_p.h>
#include <qatomic.h>
#include <qstring.h>
#include <qendian.h>
#include <qnumeric.h>
#include "private/qendian_p.h"
#include "private/qsimd_p.h"
#include <limits.h>
#include <limits>
#include <type_traits>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// in qstring.cpp
void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len);
void qt_from_latin1(ushort *dst, const char *str, size_t size) noexcept;
/*
This defines a binary data structure for Json data. The data structure is optimised for fast reading
and minimum allocations. The whole data structure can be mmap'ed and used directly.
In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but
much faster to access.
The size requirements are:
String:
Latin1 data: 2 bytes header + string.length()
Full Unicode: 4 bytes header + 2*(string.length())
Values: 4 bytes + size of data (size can be 0 for some data)
bool: 0 bytes
double: 8 bytes (0 if integer with less than 27bits)
string: see above
array: size of array
object: size of object
Array: 12 bytes + 4*length + size of Value data
Object: 12 bytes + 8*length + size of Key Strings + size of Value data
For an example such as
{ // object: 12 + 5*8 = 52
"firstName": "John", // key 12, value 8 = 20
"lastName" : "Smith", // key 12, value 8 = 20
"age" : 25, // key 8, value 0 = 8
"address" : // key 12, object below = 140
{ // object: 12 + 4*8
"streetAddress": "21 2nd Street", // key 16, value 16
"city" : "New York", // key 8, value 12
"state" : "NY", // key 8, value 4
"postalCode" : "10021" // key 12, value 8
}, // object total: 128
"phoneNumber": // key: 16, value array below = 172
[ // array: 12 + 2*4 + values below: 156
{ // object 12 + 2*8
"type" : "home", // key 8, value 8
"number": "212 555-1234" // key 8, value 16
}, // object total: 68
{ // object 12 + 2*8
"type" : "fax", // key 8, value 8
"number": "646 555-4567" // key 8, value 16
} // object total: 68
] // array total: 156
} // great total: 412 bytes
The uncompressed text file used roughly 500 bytes, so in this case we end up using about
the same space as the text representation.
Other measurements have shown a slightly bigger binary size than a compact text
representation where all possible whitespace was stripped out.
*/
#define Q_DECLARE_JSONPRIVATE_TYPEINFO(Class, Flags) } Q_DECLARE_TYPEINFO(QJsonPrivate::Class, Flags); namespace QJsonPrivate {
namespace QJsonPrivate { namespace QJsonPrivate {
class Array; template<typename Element, typename ElementsIterator>
class Object; struct ObjectIterator
class Value;
class Entry;
template<typename T>
using q_littleendian = QLEInteger<T>;
typedef q_littleendian<short> qle_short;
typedef q_littleendian<unsigned short> qle_ushort;
typedef q_littleendian<int> qle_int;
typedef q_littleendian<unsigned int> qle_uint;
template<int pos, int width>
using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
template<int pos, int width>
using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
typedef qle_uint offset;
// round the size up to the next 4 byte boundary
inline int alignedSize(int size) { return (size + 3) & ~3; }
const int MaxLatin1Length = 0x7fff;
static inline bool useCompressed(QStringView s)
{ {
if (s.length() > MaxLatin1Length) using pointer = Element *;
return false;
return QtPrivate::isLatin1(s);
}
static inline bool useCompressed(QLatin1String s) struct value_type;
{ struct reference {
return s.size() <= MaxLatin1Length; reference(Element &ref) : m_key(&ref) {}
}
template <typename T> reference() = delete;
static inline int qStringSize(T string, bool compress) ~reference() = default;
{
int l = 2 + string.size();
if (!compress)
l *= 2;
return alignedSize(l);
}
// returns INT_MAX if it can't compress it into 28 bits reference(const reference &other) = default;
static inline int compressedNumber(double d) reference(reference &&other) = default;
{
// this relies on details of how ieee floats are represented
const int exponent_off = 52;
const quint64 fraction_mask = 0x000fffffffffffffull;
const quint64 exponent_mask = 0x7ff0000000000000ull;
quint64 val; reference &operator=(const value_type &value);
memcpy (&val, &d, sizeof(double)); reference &operator=(const reference &other)
int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; {
if (exp < 0 || exp > 25) if (m_key != other.m_key) {
return INT_MAX; key() = other.key();
value() = other.value();
quint64 non_int = val & (fraction_mask >> exp); }
if (non_int) return *this;
return INT_MAX;
bool neg = (val >> 63) != 0;
val &= fraction_mask;
val |= ((quint64)1 << 52);
int res = (int)(val >> (52 - exp));
return neg ? -res : res;
}
class Latin1String;
class String
{
public:
explicit String(const char *data) { d = (Data *)data; }
struct Data {
qle_uint length;
qle_ushort utf16[1];
};
Data *d;
int byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; }
bool isValid(int maxSize) const {
// Check byteSize() <= maxSize, avoiding integer overflow
maxSize -= sizeof(uint);
return maxSize >= 0 && uint(d->length) <= maxSize / sizeof(ushort);
}
inline String &operator=(QStringView str)
{
d->length = str.length();
qToLittleEndian<quint16>(str.utf16(), str.length(), d->utf16);
fillTrailingZeros();
return *this;
}
inline String &operator=(QLatin1String str)
{
d->length = str.size();
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
for (int i = 0; i < str.size(); ++i)
d->utf16[i] = str[i].unicode();
#else
qt_from_latin1((ushort *)d->utf16, str.data(), str.size());
#endif
fillTrailingZeros();
return *this;
}
void fillTrailingZeros()
{
if (d->length & 1)
d->utf16[d->length] = 0;
}
inline bool operator ==(QStringView str) const {
int slen = str.length();
int l = d->length;
if (slen != l)
return false;
const ushort *s = (const ushort *)str.utf16();
const qle_ushort *a = d->utf16;
const ushort *b = s;
while (l-- && *a == *b)
a++,b++;
return (l == -1);
}
inline bool operator !=(QStringView str) const {
return !operator ==(str);
}
inline bool operator >=(QStringView str) const {
// ###
return toString() >= str;
}
inline bool operator<(const Latin1String &str) const;
inline bool operator>=(const Latin1String &str) const { return !operator <(str); }
inline bool operator ==(const Latin1String &str) const;
inline bool operator ==(const String &str) const {
if (d->length != str.d->length)
return false;
return !memcmp(d->utf16, str.d->utf16, d->length*sizeof(ushort));
}
inline bool operator<(const String &other) const;
inline bool operator >=(const String &other) const { return !(*this < other); }
inline QString toString() const {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
return QString((QChar *)d->utf16, d->length);
#else
int l = d->length;
QString str(l, Qt::Uninitialized);
QChar *ch = str.data();
for (int i = 0; i < l; ++i)
ch[i] = QChar(d->utf16[i]);
return str;
#endif
}
};
class Latin1String
{
public:
explicit Latin1String(const char *data) { d = (Data *)data; }
struct Data {
qle_ushort length;
char latin1[1];
};
Data *d;
int byteSize() const { return sizeof(ushort) + sizeof(char)*(d->length); }
bool isValid(int maxSize) const {
return byteSize() <= maxSize;
}
inline Latin1String &operator=(QStringView str)
{
int len = d->length = str.length();
uchar *l = (uchar *)d->latin1;
const ushort *uc = (const ushort *)str.utf16();
qt_to_latin1_unchecked(l, uc, len);
fillTrailingZeros();
return *this;
}
inline Latin1String &operator=(QLatin1String str)
{
int len = d->length = str.size();
uchar *l = (uchar *)d->latin1;
memcpy(l, str.data(), len);
fillTrailingZeros();
return *this;
}
void fillTrailingZeros()
{
uchar *l = (uchar *)d->latin1;
for (int len = d->length; (quintptr)(l + len) & 0x3; ++len)
l[len] = 0;
}
QLatin1String toQLatin1String() const noexcept {
return QLatin1String(d->latin1, d->length);
}
inline bool operator<(const String &str) const
{
const qle_ushort *uc = (qle_ushort *) str.d->utf16;
if (!uc || *uc == 0)
return false;
const uchar *c = (uchar *)d->latin1;
const uchar *e = c + qMin((int)d->length, (int)str.d->length);
while (c < e) {
if (*c != *uc)
break;
++c;
++uc;
} }
return (c == e ? (int)d->length < (int)str.d->length : *c < *uc);
} reference &operator=(reference &&other)
inline bool operator ==(const String &str) const { {
return (str == *this); key() = other.key();
} value() = other.value();
inline bool operator >=(const String &str) const { return *this;
return !(*this < str); }
}
inline QString toString() const { Element &key() { return *m_key; }
return QString::fromLatin1(d->latin1, d->length); Element &value() { return *(m_key + 1); }
}
};
#define DEF_OP(op) \ const Element &key() const { return *m_key; }
inline bool operator op(Latin1String lhs, Latin1String rhs) noexcept \ const Element &value() const { return *(m_key + 1); }
{ \
return lhs.toQLatin1String() op rhs.toQLatin1String(); \
} \
inline bool operator op(QLatin1String lhs, Latin1String rhs) noexcept \
{ \
return lhs op rhs.toQLatin1String(); \
} \
inline bool operator op(Latin1String lhs, QLatin1String rhs) noexcept \
{ \
return lhs.toQLatin1String() op rhs; \
} \
inline bool operator op(QStringView lhs, Latin1String rhs) noexcept \
{ \
return lhs op rhs.toQLatin1String(); \
} \
inline bool operator op(Latin1String lhs, QStringView rhs) noexcept \
{ \
return lhs.toQLatin1String() op rhs; \
} \
/*end*/
DEF_OP(==)
DEF_OP(!=)
DEF_OP(< )
DEF_OP(> )
DEF_OP(<=)
DEF_OP(>=)
#undef DEF_OP
inline bool String::operator ==(const Latin1String &str) const
{
if ((int)d->length != (int)str.d->length)
return false;
const qle_ushort *uc = d->utf16;
const qle_ushort *e = uc + d->length;
const uchar *c = (uchar *)str.d->latin1;
while (uc < e) {
if (*uc != *c)
return false;
++uc;
++c;
}
return true;
}
inline bool String::operator <(const String &other) const
{
int alen = d->length;
int blen = other.d->length;
int l = qMin(alen, blen);
qle_ushort *a = d->utf16;
qle_ushort *b = other.d->utf16;
while (l-- && *a == *b)
a++,b++;
if (l==-1)
return (alen < blen);
return (ushort)*a < (ushort)*b;
}
inline bool String::operator<(const Latin1String &str) const
{
const uchar *c = (uchar *) str.d->latin1;
if (!c || *c == 0)
return false;
const qle_ushort *uc = d->utf16;
const qle_ushort *e = uc + qMin((int)d->length, (int)str.d->length);
while (uc < e) {
if (*uc != *c)
break;
++uc;
++c;
}
return (uc == e ? (int)d->length < (int)str.d->length : (ushort)*uc < *c);
}
template <typename T>
static inline void copyString(char *dest, T str, bool compress)
{
if (compress) {
Latin1String string(dest);
string = str;
} else {
String string(dest);
string = str;
}
}
/* private:
Base is the base class for both Object and Array. Both classes work more or less the same way. Element *m_key;
The class starts with a header (defined by the struct below), then followed by data (the data for
values in the Array case and Entry's (see below) for objects.
After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and
offsets from the beginning of the object to Entry's in the case of Object.
Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage
of a binary search over the keys in an Object.
*/
class Base
{
public:
qle_uint size;
union {
uint _dummy;
qle_bitfield<0, 1> is_object;
qle_bitfield<1, 31> length;
}; };
offset tableOffset;
// content follows here
inline bool isObject() const { return !!is_object; } struct value_type {
inline bool isArray() const { return !isObject(); } value_type(reference ref) : m_key(ref.key()), m_value(ref.value()) {}
inline offset *table() const { return (offset *) (((char *) this) + tableOffset); } Element key() const { return m_key; }
Element value() const { return m_value; }
private:
Element m_key;
Element m_value;
};
int reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace); using difference_type = typename QVector<Element>::difference_type;
void removeItems(int pos, int numItems); using iterator_category = std::random_access_iterator_tag;
ObjectIterator() = default;
ObjectIterator(ElementsIterator it) : it(it) {}
ElementsIterator elementsIterator() { return it; }
ObjectIterator operator++(int) { ObjectIterator ret(it); it += 2; return ret; }
ObjectIterator &operator++() { it += 2; return *this; }
ObjectIterator &operator+=(difference_type n) { it += 2 * n; return *this; }
ObjectIterator operator--(int) { ObjectIterator ret(it); it -= 2; return ret; }
ObjectIterator &operator--() { it -= 2; return *this; }
ObjectIterator &operator-=(difference_type n) { it -= 2 * n; return *this; }
reference operator*() const { return *it; }
reference operator[](int n) const { return it[n * 2]; }
bool operator<(ObjectIterator other) const { return it < other.it; }
bool operator>(ObjectIterator other) const { return it > other.it; }
bool operator<=(ObjectIterator other) const { return it <= other.it; }
bool operator>=(ObjectIterator other) const { return it >= other.it; }
private:
ElementsIterator it;
}; };
class Object : public Base template<typename Element, typename ElementsIterator>
inline ObjectIterator<Element, ElementsIterator> operator+(
ObjectIterator<Element, ElementsIterator> a,
typename ObjectIterator<Element, ElementsIterator>::difference_type n)
{ {
public: return {a.elementsIterator() + 2 * n};
Entry *entryAt(int i) const { }
return reinterpret_cast<Entry *>(((char *)this) + table()[i]); template<typename Element, typename ElementsIterator>
} inline ObjectIterator<Element, ElementsIterator> operator+(
int indexOf(QStringView key, bool *exists) const; int n, ObjectIterator<Element, ElementsIterator> a)
int indexOf(QLatin1String key, bool *exists) const;
bool isValid(int maxSize) const;
};
class Array : public Base
{ {
public: return {a.elementsIterator() + 2 * n};
inline Value at(int i) const; }
inline Value &operator [](int i); template<typename Element, typename ElementsIterator>
inline ObjectIterator<Element, ElementsIterator> operator-(
ObjectIterator<Element, ElementsIterator> a,
typename ObjectIterator<Element, ElementsIterator>::difference_type n)
{
return {a.elementsIterator() - 2 * n};
}
template<typename Element, typename ElementsIterator>
inline int operator-(
ObjectIterator<Element, ElementsIterator> a,
ObjectIterator<Element, ElementsIterator> b)
{
return (a.elementsIterator() - b.elementsIterator()) / 2;
}
template<typename Element, typename ElementsIterator>
inline bool operator!=(
ObjectIterator<Element, ElementsIterator> a,
ObjectIterator<Element, ElementsIterator> b)
{
return a.elementsIterator() != b.elementsIterator();
}
template<typename Element, typename ElementsIterator>
inline bool operator==(
ObjectIterator<Element, ElementsIterator> a,
ObjectIterator<Element, ElementsIterator> b)
{
return a.elementsIterator() == b.elementsIterator();
}
bool isValid(int maxSize) const; using KeyIterator = ObjectIterator<QtCbor::Element, QVector<QtCbor::Element>::iterator>;
}; using ConstKeyIterator = ObjectIterator<const QtCbor::Element, QVector<QtCbor::Element>::const_iterator>;
template<>
inline KeyIterator::reference &KeyIterator::reference::operator=(const KeyIterator::value_type &value)
{
*m_key = value.key();
*(m_key + 1) = value.value();
return *this;
}
inline void swap(KeyIterator::reference a, KeyIterator::reference b)
{
KeyIterator::value_type t = a;
a = b;
b = t;
}
class Value class Value
{ {
public: public:
enum { static QCborContainerPrivate *container(const QCborValue &v) { return v.container; }
MaxSize = (1<<27) - 1
};
union {
uint _dummy;
qle_bitfield<0, 3> type;
qle_bitfield<3, 1> latinOrIntValue;
qle_bitfield<4, 1> latinKey;
qle_bitfield<5, 27> value;
qle_signedbitfield<5, 27> int_value;
};
inline char *data(const Base *b) const { return ((char *)b) + value; } static QJsonValue fromTrustedCbor(const QCborValue &v)
int usedStorage(const Base *b) const;
bool toBoolean() const;
double toDouble(const Base *b) const;
QString toString(const Base *b) const;
String asString(const Base *b) const;
Latin1String asLatin1String(const Base *b) const;
Base *base(const Base *b) const;
bool isValid(const Base *b) const;
static int requiredStorage(QJsonValue &v, bool *compressed);
static uint valueToStore(const QJsonValue &v, uint offset);
static void copyData(const QJsonValue &v, char *dest, bool compressed);
};
Q_DECLARE_JSONPRIVATE_TYPEINFO(Value, Q_PRIMITIVE_TYPE)
inline Value Array::at(int i) const
{
return *(Value *) (table() + i);
}
inline Value &Array::operator [](int i)
{
return *(Value *) (table() + i);
}
class Entry {
public:
Value value;
// key
// value data follows key
uint size() const {
int s = sizeof(Entry);
if (value.latinKey)
s += shallowLatin1Key().byteSize();
else
s += shallowKey().byteSize();
return alignedSize(s);
}
int usedStorage(Base *b) const {
return size() + value.usedStorage(b);
}
String shallowKey() const
{ {
Q_ASSERT(!value.latinKey); QJsonValue result;
return String((const char *)this + sizeof(Entry)); result.d = v.container;
result.n = v.n;
result.t = v.t;
return result;
} }
Latin1String shallowLatin1Key() const
{
Q_ASSERT(value.latinKey);
return Latin1String((const char *)this + sizeof(Entry));
}
QString key() const
{
if (value.latinKey) {
return shallowLatin1Key().toString();
}
return shallowKey().toString();
}
bool isValid(int maxSize) const {
if (maxSize < (int)sizeof(Entry))
return false;
maxSize -= sizeof(Entry);
if (value.latinKey)
return shallowLatin1Key().isValid(maxSize);
return shallowKey().isValid(maxSize);
}
bool operator ==(QStringView key) const;
inline bool operator !=(QStringView key) const { return !operator ==(key); }
inline bool operator >=(QStringView key) const;
bool operator==(QLatin1String key) const;
inline bool operator!=(QLatin1String key) const { return !operator ==(key); }
inline bool operator>=(QLatin1String key) const;
bool operator ==(const Entry &other) const;
bool operator >=(const Entry &other) const;
}; };
inline bool Entry::operator >=(QStringView key) const } // namespace QJsonPrivate
{
if (value.latinKey)
return (shallowLatin1Key() >= key);
else
return (shallowKey() >= key);
}
inline bool Entry::operator >=(QLatin1String key) const
{
if (value.latinKey)
return shallowLatin1Key() >= key;
else
return shallowKey() >= QString(key); // ### conversion to QString
}
inline bool operator <(QStringView key, const Entry &e)
{ return e >= key; }
inline bool operator<(QLatin1String key, const Entry &e)
{ return e >= key; }
class Header {
public:
qle_uint tag; // 'qbjs'
qle_uint version; // 1
Base *root() { return (Base *)(this + 1); }
};
inline bool Value::toBoolean() const
{
Q_ASSERT(type == QJsonValue::Bool);
return value != 0;
}
inline double Value::toDouble(const Base *b) const
{
Q_ASSERT(type == QJsonValue::Double);
if (latinOrIntValue)
return int_value;
quint64 i = qFromLittleEndian<quint64>((const uchar *)b + value);
double d;
memcpy(&d, &i, sizeof(double));
return d;
}
inline String Value::asString(const Base *b) const
{
Q_ASSERT(type == QJsonValue::String && !latinOrIntValue);
return String(data(b));
}
inline Latin1String Value::asLatin1String(const Base *b) const
{
Q_ASSERT(type == QJsonValue::String && latinOrIntValue);
return Latin1String(data(b));
}
inline QString Value::toString(const Base *b) const
{
if (latinOrIntValue)
return asLatin1String(b).toString();
else
return asString(b).toString();
}
inline Base *Value::base(const Base *b) const
{
Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object);
return reinterpret_cast<Base *>(data(b));
}
class Data {
public:
enum Validation {
Unchecked,
Validated,
Invalid
};
QAtomicInt ref;
int alloc;
union {
char *rawData;
Header *header;
};
uint compactionCounter : 31;
uint ownsData : 1;
inline Data(char *raw, int a)
: alloc(a), rawData(raw), compactionCounter(0), ownsData(true)
{
}
inline Data(int reserved, QJsonValue::Type valueType)
: rawData(nullptr), compactionCounter(0), ownsData(true)
{
Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object);
alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset);
header = (Header *)malloc(alloc);
Q_CHECK_PTR(header);
header->tag = QJsonDocument::BinaryFormatTag;
header->version = 1;
Base *b = header->root();
b->size = sizeof(Base);
b->is_object = (valueType == QJsonValue::Object);
b->tableOffset = sizeof(Base);
b->length = 0;
}
inline ~Data()
{ if (ownsData) free(rawData); }
uint offsetOf(const void *ptr) const { return (uint)(((char *)ptr - rawData)); }
QJsonObject toObject(Object *o) const
{
return QJsonObject(const_cast<Data *>(this), o);
}
QJsonArray toArray(Array *a) const
{
return QJsonArray(const_cast<Data *>(this), a);
}
Data *clone(Base *b, int reserve = 0)
{
int size = sizeof(Header) + b->size;
if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve)
return this;
if (reserve) {
if (reserve < 128)
reserve = 128;
size = qMax(size + reserve, qMin(size *2, (int)Value::MaxSize));
if (size > Value::MaxSize) {
qWarning("QJson: Document too large to store in data structure");
return nullptr;
}
}
char *raw = (char *)malloc(size);
Q_CHECK_PTR(raw);
memcpy(raw + sizeof(Header), b, b->size);
Header *h = (Header *)raw;
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1;
Data *d = new Data(raw, size);
d->compactionCounter = (b == header->root()) ? compactionCounter : 0;
return d;
}
void compact();
bool valid() const;
private:
Q_DISABLE_COPY_MOVE(Data)
};
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

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

View File

@ -42,6 +42,7 @@
#include <QtCore/qjsonvalue.h> #include <QtCore/qjsonvalue.h>
#include <QtCore/qiterator.h> #include <QtCore/qiterator.h>
#include <QtCore/qshareddata.h>
#include <initializer_list> #include <initializer_list>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -56,25 +57,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
{ {
@ -113,7 +103,6 @@ public:
void swap(QJsonArray &other) noexcept void swap(QJsonArray &other) noexcept
{ {
qSwap(d, other.d);
qSwap(a, other.a); qSwap(a, other.a);
} }
@ -245,20 +234,21 @@ public:
typedef int difference_type; typedef int difference_type;
private: private:
friend class QJsonPrivate::Data;
friend class QJsonValue; friend class QJsonValue;
friend class QJsonDocument; friend class QJsonDocument;
friend class QCborArray;
friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &);
QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array); QJsonArray(QCborContainerPrivate *array);
void initialize(); void initialize();
void compact(); void compact();
// ### Qt 6: remove me and merge with detach2 // ### Qt 6: remove me and merge with detach2
void detach(uint reserve = 0); void detach(uint reserve = 0);
bool detach2(uint reserve = 0); bool detach2(uint reserve = 0);
QJsonPrivate::Data *d; // ### Qt 6: remove
QJsonPrivate::Array *a; void *dead = nullptr;
QExplicitlySharedDataPointer<QCborContainerPrivate> a;
}; };
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray) Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,8 +52,8 @@
// //
#include <QtCore/private/qglobal_p.h> #include <QtCore/private/qglobal_p.h>
#include <qjsondocument.h> #include <QtCore/private/qcborvalue_p.h>
#include <qvarlengtharray.h> #include <QtCore/qjsondocument.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -64,25 +64,7 @@ class Parser
public: public:
Parser(const char *json, int length); Parser(const char *json, int length);
QJsonDocument parse(QJsonParseError *error); QCborValue parse(QJsonParseError *error);
class ParsedObject
{
public:
ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) {
offsets.reserve(64);
}
void insert(uint offset);
Parser *parser;
int objectPosition;
QVector<uint> offsets;
inline QJsonPrivate::Entry *entryAt(int i) const {
return reinterpret_cast<QJsonPrivate::Entry *>(parser->data + objectPosition + offsets[i]);
}
};
private: private:
inline void eatBOM(); inline void eatBOM();
@ -91,34 +73,17 @@ private:
bool parseObject(); bool parseObject();
bool parseArray(); bool parseArray();
bool parseMember(int baseOffset); bool parseMember();
bool parseString(bool *latin1); bool parseString();
bool parseValue(QJsonPrivate::Value *val, int baseOffset); bool parseValue();
bool parseNumber(QJsonPrivate::Value *val, int baseOffset); bool parseNumber();
const char *head; const char *head;
const char *json; const char *json;
const char *end; const char *end;
char *data;
int dataLength;
int current;
int nestingLevel; int nestingLevel;
QJsonParseError::ParseError lastError; QJsonParseError::ParseError lastError;
QExplicitlySharedDataPointer<QCborContainerPrivate> container;
inline int reserveSpace(int space) {
if (current + space >= dataLength) {
dataLength = 2*dataLength + space;
char *newData = (char *)realloc(data, dataLength);
if (!newData) {
lastError = QJsonParseError::DocumentTooLarge;
return -1;
}
data = newData;
}
int pos = current;
current += space;
return pos;
}
}; };
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 \