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 \
qfilesystementry.o qfsfileengine.o qfsfileengine_iterator.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 \
quuid.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/qvariant.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/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/qjsonobject.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
$(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) $<
qjsondocument.o: $(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp

View File

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

View File

@ -116,6 +116,7 @@ SOURCES += \
qbytearray.cpp \
qbytearraymatcher.cpp \
qcalendar.cpp \
qcborvalue.cpp \
qcryptographichash.cpp \
qdatetime.cpp \
qdir.cpp \
@ -131,8 +132,8 @@ SOURCES += \
qgregoriancalendar.cpp \
qhash.cpp \
qiodevice.cpp \
qjson.cpp \
qjsonarray.cpp \
qjsoncbor.cpp \
qjsondocument.cpp \
qjsonobject.cpp \
qjsonparser.cpp \
@ -174,6 +175,8 @@ HEADERS += \
qcalendar.h \
qcalendarbackend_p.h \
qcalendarmath_p.h \
qcborvalue.h \
qcborvalue_p.h \
qchar.h \
qcryptographichash.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.",
"section": "Utilities",
"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
# define QT_FEATURE_alloca_malloc_h -1
#endif
#define QT_FEATURE_binaryjson -1
#define QT_FEATURE_cborstream -1
#define QT_CRYPTOGRAPHICHASH_ONLY_SHA1
#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 "qcbormap.cpp"
#ifndef QT_NO_QOBJECT
#include "moc_qcborvalue.cpp"
#endif

View File

@ -71,6 +71,8 @@ class QCborStreamReader;
class QCborStreamWriter;
class QDataStream;
namespace QJsonPrivate { class Value; }
struct QCborParserError
{
qint64 offset = 0;
@ -301,6 +303,8 @@ public:
private:
friend class QCborValueRef;
friend class QCborContainerPrivate;
friend class QJsonPrivate::Value;
qint64 n = 0;
QCborContainerPrivate *container = nullptr;
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.
//
#include <qjsonobject.h>
#include <qjsonvalue.h>
#include <qjsondocument.h>
#include <qjsonarray.h>
#include <qatomic.h>
#include <qstring.h>
#include <qendian.h>
#include <qnumeric.h>
#include "private/qendian_p.h"
#include "private/qsimd_p.h"
#include <limits.h>
#include <limits>
#include <type_traits>
#include <qcborvalue.h>
#include <private/qcborvalue_p.h>
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 {
class Array;
class Object;
class Value;
class Entry;
template<typename T>
using q_littleendian = QLEInteger<T>;
typedef q_littleendian<short> qle_short;
typedef q_littleendian<unsigned short> qle_ushort;
typedef q_littleendian<int> qle_int;
typedef q_littleendian<unsigned int> qle_uint;
template<int pos, int width>
using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
template<int pos, int width>
using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
typedef qle_uint offset;
// round the size up to the next 4 byte boundary
inline int alignedSize(int size) { return (size + 3) & ~3; }
const int MaxLatin1Length = 0x7fff;
static inline bool useCompressed(QStringView s)
template<typename Element, typename ElementsIterator>
struct ObjectIterator
{
if (s.length() > MaxLatin1Length)
return false;
return QtPrivate::isLatin1(s);
}
using pointer = Element *;
static inline bool useCompressed(QLatin1String s)
{
return s.size() <= MaxLatin1Length;
}
struct value_type;
struct reference {
reference(Element &ref) : m_key(&ref) {}
template <typename T>
static inline int qStringSize(T string, bool compress)
{
int l = 2 + string.size();
if (!compress)
l *= 2;
return alignedSize(l);
}
reference() = delete;
~reference() = default;
// 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;
reference(const reference &other) = default;
reference(reference &&other) = default;
quint64 val;
memcpy (&val, &d, sizeof(double));
int exp = (int)((val & exponent_mask) >> exponent_off) - 1023;
if (exp < 0 || exp > 25)
return INT_MAX;
quint64 non_int = val & (fraction_mask >> exp);
if (non_int)
return INT_MAX;
bool neg = (val >> 63) != 0;
val &= fraction_mask;
val |= ((quint64)1 << 52);
int res = (int)(val >> (52 - exp));
return neg ? -res : res;
}
class Latin1String;
class String
{
public:
explicit String(const char *data) { d = (Data *)data; }
struct Data {
qle_uint length;
qle_ushort utf16[1];
};
Data *d;
int byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; }
bool isValid(int maxSize) const {
// Check byteSize() <= maxSize, avoiding integer overflow
maxSize -= sizeof(uint);
return maxSize >= 0 && uint(d->length) <= maxSize / sizeof(ushort);
}
inline String &operator=(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;
reference &operator=(const value_type &value);
reference &operator=(const reference &other)
{
if (m_key != other.m_key) {
key() = other.key();
value() = other.value();
}
return *this;
}
return (c == e ? (int)d->length < (int)str.d->length : *c < *uc);
}
inline bool operator ==(const String &str) const {
return (str == *this);
}
inline bool operator >=(const String &str) const {
return !(*this < str);
}
reference &operator=(reference &&other)
{
key() = other.key();
value() = other.value();
return *this;
}
inline QString toString() const {
return QString::fromLatin1(d->latin1, d->length);
}
};
Element &key() { return *m_key; }
Element &value() { return *(m_key + 1); }
#define DEF_OP(op) \
inline bool operator op(Latin1String lhs, Latin1String rhs) noexcept \
{ \
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;
}
}
const Element &key() const { return *m_key; }
const Element &value() const { return *(m_key + 1); }
/*
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;
private:
Element *m_key;
};
offset tableOffset;
// content follows here
inline bool isObject() const { return !!is_object; }
inline bool isArray() const { return !isObject(); }
struct value_type {
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);
void removeItems(int pos, int numItems);
using difference_type = typename QVector<Element>::difference_type;
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:
Entry *entryAt(int i) const {
return reinterpret_cast<Entry *>(((char *)this) + table()[i]);
}
int indexOf(QStringView key, bool *exists) const;
int indexOf(QLatin1String key, bool *exists) const;
bool isValid(int maxSize) const;
};
class Array : public Base
return {a.elementsIterator() + 2 * n};
}
template<typename Element, typename ElementsIterator>
inline ObjectIterator<Element, ElementsIterator> operator+(
int n, ObjectIterator<Element, ElementsIterator> a)
{
public:
inline Value at(int i) const;
inline Value &operator [](int i);
return {a.elementsIterator() + 2 * n};
}
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
{
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;
};
static QCborContainerPrivate *container(const QCborValue &v) { return v.container; }
inline char *data(const Base *b) const { return ((char *)b) + value; }
int usedStorage(const Base *b) const;
bool toBoolean() const;
double toDouble(const Base *b) const;
QString toString(const Base *b) const;
String asString(const Base *b) const;
Latin1String asLatin1String(const Base *b) const;
Base *base(const Base *b) const;
bool isValid(const Base *b) const;
static int requiredStorage(QJsonValue &v, bool *compressed);
static uint valueToStore(const QJsonValue &v, uint offset);
static void copyData(const QJsonValue &v, char *dest, bool compressed);
};
Q_DECLARE_JSONPRIVATE_TYPEINFO(Value, Q_PRIMITIVE_TYPE)
inline Value Array::at(int i) const
{
return *(Value *) (table() + i);
}
inline Value &Array::operator [](int i)
{
return *(Value *) (table() + i);
}
class Entry {
public:
Value value;
// key
// value data follows key
uint size() const {
int s = sizeof(Entry);
if (value.latinKey)
s += shallowLatin1Key().byteSize();
else
s += shallowKey().byteSize();
return alignedSize(s);
}
int usedStorage(Base *b) const {
return size() + value.usedStorage(b);
}
String shallowKey() const
static QJsonValue fromTrustedCbor(const QCborValue &v)
{
Q_ASSERT(!value.latinKey);
return String((const char *)this + sizeof(Entry));
QJsonValue result;
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
{
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)
};
}
} // namespace QJsonPrivate
QT_END_NAMESPACE

View File

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

View File

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

View File

@ -42,6 +42,10 @@
#include "qcborarray.h"
#include "qcbormap.h"
#include "qjsonarray.h"
#include "qjsonobject.h"
#include "qjsondocument.h"
#include "qjson_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::Map:
#if defined(QT_JSON_READONLY) || defined(QT_BOOTSTRAPPED)
qFatal("Writing JSON is disabled.");
return QString();
#else
return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact);
#endif
case QCborValue::SimpleType:
break;
@ -181,30 +190,11 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety
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)
{
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)
static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
{
#ifndef QT_BUILD_QMAKE
qint64 tag = d->elements.at(0).value;
switch (tag) {
@ -225,12 +215,36 @@ static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d)
return s;
}
}
#endif
// 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
if (idx == -QCborValue::Array)
@ -248,7 +262,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
const auto &e = d->elements.at(idx);
switch (e.type) {
case QCborValue::Integer:
return qint64(e.value);
return QJsonPrivate::Value::fromTrustedCbor(e.value);
case QCborValue::ByteArray:
case QCborValue::String:
@ -264,14 +278,14 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
case QCborValue::RegularExpression:
case QCborValue::Uuid:
// 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:
return QJsonValue();
case QCborValue::Undefined:
case QCborValue::Invalid:
return QJsonValue(QJsonValue::Undefined);
return QJsonValue::Undefined;
case QCborValue::False:
return false;
@ -283,7 +297,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx)
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
{
if (container)
return convertToJson(container, n < 0 ? -type() : n);
return qt_convertToJson(container, n < 0 ? -type() : n);
// simple values
switch (type()) {
case Integer:
return n;
case Null:
return QJsonValue();
case False:
return false;
case Integer:
return QJsonPrivate::Value::fromTrustedCbor(n);
case True:
return true;
case Null:
return QJsonValue();
case Double:
return fpToJson(fp_helper());
@ -372,12 +386,12 @@ QJsonValue QCborValue::toJsonValue() const
case Undefined:
case Invalid:
return QJsonValue(QJsonValue::Undefined);
return QJsonValue::Undefined;
case ByteArray:
case String:
// empty strings
return QString();
return QJsonValue::String;
case Array:
// empty array
@ -392,16 +406,16 @@ QJsonValue QCborValue::toJsonValue() const
case Url:
case RegularExpression:
case Uuid:
Q_UNREACHABLE();
// Reachable, but invalid in Json
return QJsonValue::Undefined;
}
return simpleTypeString(type());
return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type()));
}
QJsonValue QCborValueRef::toJsonValue() const
{
return convertToJson(d, i);
return qt_convertToJson(d, i);
}
/*!
@ -540,8 +554,10 @@ QVariant QCborValue::toVariant() const
case DateTime:
return toDateTime();
#ifndef QT_BOOTSTRAPPED
case Url:
return toUrl();
#endif
#if QT_CONFIG(regularexpression)
case RegularExpression:
@ -597,12 +613,13 @@ QCborValue QCborValue::fromJsonValue(const QJsonValue &v)
{
switch (v.type()) {
case QJsonValue::Bool:
return v.b;
return v.toBool();
case QJsonValue::Double: {
qint64 i;
if (convertDoubleTo(v.dbl, &i))
const double dbl = v.toDouble();
if (convertDoubleTo(dbl, &i))
return i;
return v.dbl;
return dbl;
}
case QJsonValue::String:
return v.toString();
@ -710,8 +727,10 @@ QCborValue QCborValue::fromVariant(const QVariant &variant)
return variant.toByteArray();
case QVariant::DateTime:
return QCborValue(variant.toDateTime());
#ifndef QT_BOOTSTRAPPED
case QVariant::Url:
return QCborValue(variant.toUrl());
#endif
case QVariant::Uuid:
return QCborValue(variant.toUuid());
case QVariant::List:
@ -824,15 +843,9 @@ QCborArray QCborArray::fromVariantList(const QVariantList &list)
*/
QCborArray QCborArray::fromJsonArray(const QJsonArray &array)
{
QCborArray a;
a.detach(array.size());
for (const QJsonValue &v : array) {
if (v.isString())
a.d->append(v.toString());
else
a.d->append(QCborValue::fromJsonValue(v));
}
return a;
QCborArray result;
result.d = array.a;
return result;
}
/*!
@ -944,20 +957,9 @@ QCborMap QCborMap::fromVariantHash(const QVariantHash &hash)
*/
QCborMap QCborMap::fromJsonObject(const QJsonObject &obj)
{
QCborMap m;
m.detach(obj.size());
QCborContainerPrivate *d = m.d.data();
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;
QCborMap result;
result.d = obj.o;
return result;
}
QT_END_NAMESPACE

View File

@ -44,11 +44,22 @@
#include <qstringlist.h>
#include <qvariant.h>
#include <qdebug.h>
#include <qcbormap.h>
#include <qcborarray.h>
#include "qcborvalue_p.h"
#include "qjsonwriter_p.h"
#include "qjsonparser_p.h"
#include "qjson_p.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
/*! \class QJsonDocument
@ -80,6 +91,33 @@ QT_BEGIN_NAMESPACE
\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.
*/
@ -109,11 +147,10 @@ QJsonDocument::QJsonDocument(const QJsonArray &array)
/*!
\internal
*/
QJsonDocument::QJsonDocument(QJsonPrivate::Data *data)
: d(data)
QJsonDocument::QJsonDocument(const QCborValue &data)
: d(qt_make_unique<QJsonDocumentPrivate>(data))
{
Q_ASSERT(d);
d->ref.ref();
}
/*!
@ -121,20 +158,30 @@ QJsonDocument::QJsonDocument(QJsonPrivate::Data *data)
Binary data set with fromRawData is not freed.
*/
QJsonDocument::~QJsonDocument()
{
if (d && !d->ref.deref())
delete d;
}
QJsonDocument::~QJsonDocument() = default;
/*!
* Creates a copy of the \a other document.
*/
QJsonDocument::QJsonDocument(const QJsonDocument &other)
{
d = other.d;
if (d)
d->ref.ref();
if (other.d) {
if (!d)
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)
{
if (d != other.d) {
if (d && !d->ref.deref())
delete d;
d = other.d;
if (d)
d->ref.ref();
if (this != &other) {
if (other.d) {
if (!d)
d = qt_make_unique<QJsonDocumentPrivate>();
else
d->clearRawData();
d->value = other.d->value;
} else {
d.reset();
}
}
return *this;
}
@ -187,12 +237,13 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
the application.
*/
#if QT_CONFIG(binaryjson)
/*!
Creates a QJsonDocument that uses the first \a size bytes from
\a data. It assumes \a data contains a binary encoded JSON document.
The created document does not take ownership of \a data and the caller
has to guarantee that \a data will not be deleted or modified as long as
any QJsonDocument, QJsonObject or QJsonArray still references the data.
The created document does not take ownership of \a data. The data is
copied into a different data structure, and the original data can be
deleted or modified afterwards.
\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.
\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)
{
@ -211,18 +273,15 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat
return QJsonDocument();
}
if (size < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)))
if (size < 0 || uint(size) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base))
return QJsonDocument();
QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size);
d->ownsData = false;
std::unique_ptr<QBinaryJsonPrivate::ConstData> binaryData
= qt_make_unique<QBinaryJsonPrivate::ConstData>(data, size);
if (validation != BypassValidation && !d->valid()) {
delete d;
return QJsonDocument();
}
return QJsonDocument(d);
return (validation == BypassValidation || binaryData->isValid())
? binaryData->toJsonDocument()
: QJsonDocument();
}
/*!
@ -230,7 +289,16 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat
\a size will contain the size of the returned data.
This method is useful to e.g. stream the JSON document
in it's binary form to a file.
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
{
@ -238,7 +306,21 @@ const char *QJsonDocument::rawData(int *size) const
*size = 0;
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;
}
@ -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
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)
{
if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)))
if (uint(data.size()) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base))
return QJsonDocument();
QJsonPrivate::Header h;
memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header));
QJsonPrivate::Base root;
memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base));
QBinaryJsonPrivate::Header h;
memcpy(&h, data.constData(), sizeof(QBinaryJsonPrivate::Header));
QBinaryJsonPrivate::Base root;
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.
if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u ||
sizeof(QJsonPrivate::Header) + root.size > (uint)data.size())
const uint size = sizeof(QBinaryJsonPrivate::Header) + root.size;
if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1U || size > uint(data.size()))
return QJsonDocument();
const uint size = sizeof(QJsonPrivate::Header) + root.size;
char *raw = (char *)malloc(size);
if (!raw)
return QJsonDocument();
std::unique_ptr<QBinaryJsonPrivate::ConstData> d
= qt_make_unique<QBinaryJsonPrivate::ConstData>(data.constData(), size);
memcpy(raw, data.constData(), size);
QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size);
if (validation != BypassValidation && !d->valid()) {
delete d;
return QJsonDocument();
}
return QJsonDocument(d);
return (validation == BypassValidation || d->isValid())
? d->toJsonDocument()
: QJsonDocument();
}
/*!
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.
@ -293,6 +401,7 @@ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidati
QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
{
QJsonDocument doc;
switch (variant.type()) {
case QVariant::Map:
doc.setObject(QJsonObject::fromVariantMap(variant.toMap()));
@ -304,7 +413,8 @@ QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
doc.setArray(QJsonArray::fromVariantList(variant.toList()));
break;
case QVariant::StringList:
doc.setArray(QJsonArray::fromStringList(variant.toStringList()));
doc.d = qt_make_unique<QJsonDocumentPrivate>();
doc.d->value = QCborArray::fromStringList(variant.toStringList());
break;
default:
break;
@ -325,10 +435,10 @@ QVariant QJsonDocument::toVariant() const
if (!d)
return QVariant();
if (d->header->root()->isArray())
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root())).toVariantList();
else
return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root())).toVariantMap();
QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
if (d->value.isArray())
return QJsonArray(container).toVariantList();
return QJsonObject(container).toVariantMap();
}
/*!
@ -370,10 +480,11 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const
if (!d)
return json;
if (d->header->root()->isArray())
QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(d->header->root()), json, 0, (format == Compact));
const QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
if (d->value.isArray())
QJsonPrivate::Writer::arrayToJson(container, json, 0, (format == Compact));
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;
}
@ -392,7 +503,13 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const
QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
{
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;
}
/*!
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.
@ -436,8 +533,7 @@ bool QJsonDocument::isArray() const
if (!d)
return false;
QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData;
return h->root()->isArray();
return d->value.isArray();
}
/*!
@ -450,8 +546,7 @@ bool QJsonDocument::isObject() const
if (!d)
return false;
QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData;
return h->root()->isObject();
return d->value.isMap();
}
/*!
@ -464,10 +559,9 @@ bool QJsonDocument::isObject() const
*/
QJsonObject QJsonDocument::object() const
{
if (d) {
QJsonPrivate::Base *b = d->header->root();
if (b->isObject())
return QJsonObject(d, static_cast<QJsonPrivate::Object *>(b));
if (isObject()) {
if (auto container = QJsonPrivate::Value::container(d->value))
return QJsonObject(container);
}
return QJsonObject();
}
@ -482,10 +576,9 @@ QJsonObject QJsonDocument::object() const
*/
QJsonArray QJsonDocument::array() const
{
if (d) {
QJsonPrivate::Base *b = d->header->root();
if (b->isArray())
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(b));
if (isArray()) {
if (auto container = QJsonPrivate::Value::container(d->value))
return QJsonArray(container);
}
return QJsonArray();
}
@ -497,24 +590,12 @@ QJsonArray QJsonDocument::array() const
*/
void QJsonDocument::setObject(const QJsonObject &object)
{
if (d && !d->ref.deref())
delete d;
if (!d)
d = qt_make_unique<QJsonDocumentPrivate>();
else
d->clearRawData();
d = object.d;
if (!d) {
d = new QJsonPrivate::Data(0, QJsonValue::Object);
} else if (d->compactionCounter || object.o != d->header->root()) {
QJsonObject o(object);
if (d->compactionCounter)
o.compact();
else
o.detach2();
d = o.d;
d->ref.ref();
return;
}
d->ref.ref();
d->value = QCborValue::fromJsonValue(object);
}
/*!
@ -524,24 +605,12 @@ void QJsonDocument::setObject(const QJsonObject &object)
*/
void QJsonDocument::setArray(const QJsonArray &array)
{
if (d && !d->ref.deref())
delete d;
if (!d)
d = qt_make_unique<QJsonDocumentPrivate>();
else
d->clearRawData();
d = array.d;
if (!d) {
d = new QJsonPrivate::Data(0, QJsonValue::Array);
} else if (d->compactionCounter || array.a != d->header->root()) {
QJsonArray a(array);
if (d->compactionCounter)
a.compact();
else
a.detach2();
d = a.d;
d->ref.ref();
return;
}
d->ref.ref();
d->value = QCborValue::fromJsonValue(array);
}
#if QT_STRINGVIEW_LEVEL < 2
@ -572,7 +641,7 @@ const QJsonValue QJsonDocument::operator[](QStringView key) const
if (!isObject())
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())
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())
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
{
if (d == other.d)
return true;
if (!d || !other.d)
return false;
if (d->header->root()->isArray() != other.d->header->root()->isArray())
return false;
if (d->header->root()->isObject())
return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root()))
== QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(other.d->header->root()));
else
return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root()))
== QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(other.d->header->root()));
return (!d) ? (!other.d) : (d->value == other.d->value);
}
/*!
@ -658,10 +713,11 @@ QDebug operator<<(QDebug dbg, const QJsonDocument &o)
return dbg;
}
QByteArray json;
if (o.d->header->root()->isArray())
QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(o.d->header->root()), json, 0, true);
const QCborContainerPrivate *container = QJsonPrivate::Value::container(o.d->value);
if (o.d->value.isArray())
QJsonPrivate::Writer::arrayToJson(container, json, 0, true);
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("
<< json.constData() // print as utf-8 string without extra quotation marks
<< ')';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -62,8 +62,8 @@ namespace QJsonPrivate
class Writer
{
public:
static void objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact = false);
static void arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact = false);
static void objectToJson(const QCborContainerPrivate *o, 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/qcborvalue.cpp \
serialization/qdatastream.cpp \
serialization/qjson.cpp \
serialization/qjsoncbor.cpp \
serialization/qjsondocument.cpp \
serialization/qjsonobject.cpp \
@ -45,6 +44,20 @@ qtConfig(cborstream): {
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 += \
serialization/qcborarray.cpp \
serialization/qcbormap.cpp

View File

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