| 1 | // Copyright (C) 2020 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qbinaryjson_p.h" |
| 5 | |
| 6 | #include <QtCore/qjsonobject.h> |
| 7 | #include <QtCore/qjsonarray.h> |
| 8 | |
| 9 | #include <private/qbinaryjsonarray_p.h> |
| 10 | #include <private/qbinaryjsonobject_p.h> |
| 11 | |
| 12 | QT_BEGIN_NAMESPACE |
| 13 | |
| 14 | /*! |
| 15 | \namespace QBinaryJson |
| 16 | \inmodule QtCore5Compat |
| 17 | \brief Contains functions for converting QJsonDocument to and from JSON binary format. |
| 18 | |
| 19 | This namespace provides utility functions to keep compatibility with |
| 20 | older code, which uses the JSON binary format for serializing JSON. Qt JSON |
| 21 | types can be converted to Qt CBOR types, which can in turn be serialized |
| 22 | into the CBOR binary format and vice versa. |
| 23 | */ |
| 24 | |
| 25 | /*! \enum QBinaryJson::DataValidation |
| 26 | |
| 27 | This enum is used to tell QJsonDocument whether to validate the binary data |
| 28 | when converting to a QJsonDocument using fromBinaryData() or fromRawData(). |
| 29 | |
| 30 | \value Validate Validate the data before using it. This is the default. |
| 31 | \value BypassValidation Bypasses data validation. Only use if you received the |
| 32 | data from a trusted place and know it's valid, as using of invalid data can crash |
| 33 | the application. |
| 34 | */ |
| 35 | |
| 36 | namespace QBinaryJson { |
| 37 | |
| 38 | /*! |
| 39 | Creates a QJsonDocument that uses the first \a size bytes from |
| 40 | \a data. It assumes \a data contains a binary encoded JSON document. |
| 41 | The created document does not take ownership of \a data. The data is |
| 42 | copied into a different data structure, and the original data can be |
| 43 | deleted or modified afterwards. |
| 44 | |
| 45 | \a data has to be aligned to a 4 byte boundary. |
| 46 | |
| 47 | \a validation decides whether the data is checked for validity before being used. |
| 48 | By default the data is validated. If the \a data is not valid, the method returns |
| 49 | a null document. |
| 50 | |
| 51 | Returns a QJsonDocument representing the data. |
| 52 | |
| 53 | \note The binary JSON encoding is only retained for backwards |
| 54 | compatibility. It is undocumented and restrictive in the maximum size of JSON |
| 55 | documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, |
| 56 | which can in turn be serialized into the CBOR binary format and vice versa. The |
| 57 | CBOR format is a well-defined and less restrictive binary representation for a |
| 58 | superset of JSON. |
| 59 | |
| 60 | \note Before Qt 5.15, the caller had to guarantee that \a data would not be |
| 61 | deleted or modified as long as any QJsonDocument, QJsonObject or QJsonArray |
| 62 | still referenced the data. From Qt 5.15 on, this is not necessary anymore. |
| 63 | |
| 64 | \sa toRawData(), fromBinaryData(), DataValidation, QCborValue |
| 65 | */ |
| 66 | QJsonDocument fromRawData(const char *data, int size, DataValidation validation) |
| 67 | { |
| 68 | if (quintptr(data) & 3) { |
| 69 | qWarning(msg: "QJsonDocument::fromRawData: data has to have 4 byte alignment" ); |
| 70 | return QJsonDocument(); |
| 71 | } |
| 72 | |
| 73 | if (size < 0 || uint(size) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base)) |
| 74 | return QJsonDocument(); |
| 75 | |
| 76 | std::unique_ptr<QBinaryJsonPrivate::ConstData> binaryData |
| 77 | = std::make_unique<QBinaryJsonPrivate::ConstData>(args&: data, args&: size); |
| 78 | |
| 79 | return (validation == BypassValidation || binaryData->isValid()) |
| 80 | ? binaryData->toJsonDocument() |
| 81 | : QJsonDocument(); |
| 82 | } |
| 83 | |
| 84 | /*! |
| 85 | Returns the raw binary representation of \a document. |
| 86 | \a size will contain the size of the returned data. |
| 87 | |
| 88 | This method is useful to e.g. stream the JSON document |
| 89 | in its binary form to a file. |
| 90 | |
| 91 | \note The binary JSON encoding is only retained for backwards |
| 92 | compatibility. It is undocumented and restrictive in the maximum size of JSON |
| 93 | documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, |
| 94 | which can in turn be serialized into the CBOR binary format and vice versa. The |
| 95 | CBOR format is a well-defined and less restrictive binary representation for a |
| 96 | superset of JSON. |
| 97 | |
| 98 | \sa fromRawData(), fromBinaryData(), toBinaryData(), QCborValue |
| 99 | */ |
| 100 | const char *toRawData(const QJsonDocument &document, int *size) |
| 101 | { |
| 102 | if (document.isNull()) { |
| 103 | *size = 0; |
| 104 | return nullptr; |
| 105 | } |
| 106 | |
| 107 | char *rawData = nullptr; |
| 108 | uint rawDataSize = 0; |
| 109 | if (document.isObject()) { |
| 110 | QBinaryJsonObject o = QBinaryJsonObject::fromJsonObject(object: document.object()); |
| 111 | rawData = o.takeRawData(size: &rawDataSize); |
| 112 | } else { |
| 113 | QBinaryJsonArray a = QBinaryJsonArray::fromJsonArray(array: document.array()); |
| 114 | rawData = a.takeRawData(size: &rawDataSize); |
| 115 | } |
| 116 | |
| 117 | // It would be quite miraculous if not, as we should have hit the 128MB limit then. |
| 118 | Q_ASSERT(rawDataSize <= uint(std::numeric_limits<int>::max())); |
| 119 | |
| 120 | *size = static_cast<int>(rawDataSize); |
| 121 | return rawData; |
| 122 | } |
| 123 | |
| 124 | /*! |
| 125 | Creates a QJsonDocument from \a data. |
| 126 | |
| 127 | \a validation decides whether the data is checked for validity before being used. |
| 128 | By default the data is validated. If the \a data is not valid, the method returns |
| 129 | a null document. |
| 130 | |
| 131 | \note The binary JSON encoding is only retained for backwards |
| 132 | compatibility. It is undocumented and restrictive in the maximum size of JSON |
| 133 | documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, |
| 134 | which can in turn be serialized into the CBOR binary format and vice versa. The |
| 135 | CBOR format is a well-defined and less restrictive binary representation for a |
| 136 | superset of JSON. |
| 137 | |
| 138 | \sa toBinaryData(), fromRawData(), DataValidation, QCborValue |
| 139 | */ |
| 140 | QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation) |
| 141 | { |
| 142 | if (uint(data.size()) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base)) |
| 143 | return QJsonDocument(); |
| 144 | |
| 145 | QBinaryJsonPrivate::Header h; |
| 146 | memcpy(dest: &h, src: data.constData(), n: sizeof(QBinaryJsonPrivate::Header)); |
| 147 | QBinaryJsonPrivate::Base root; |
| 148 | memcpy(dest: &root, src: data.constData() + sizeof(QBinaryJsonPrivate::Header), |
| 149 | n: sizeof(QBinaryJsonPrivate::Base)); |
| 150 | |
| 151 | const uint size = sizeof(QBinaryJsonPrivate::Header) + root.size; |
| 152 | if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1U || size > uint(data.size())) |
| 153 | return QJsonDocument(); |
| 154 | |
| 155 | std::unique_ptr<QBinaryJsonPrivate::ConstData> d |
| 156 | = std::make_unique<QBinaryJsonPrivate::ConstData>(args: data.constData(), args: size); |
| 157 | |
| 158 | return (validation == BypassValidation || d->isValid()) |
| 159 | ? d->toJsonDocument() |
| 160 | : QJsonDocument(); |
| 161 | } |
| 162 | |
| 163 | /*! |
| 164 | Returns a binary representation of \a document. |
| 165 | |
| 166 | The binary representation is also the native format used internally in Qt, |
| 167 | and is very efficient and fast to convert to and from. |
| 168 | |
| 169 | The binary format can be stored on disk and interchanged with other applications |
| 170 | or computers. fromBinaryData() can be used to convert it back into a |
| 171 | JSON document. |
| 172 | |
| 173 | \note The binary JSON encoding is only retained for backwards |
| 174 | compatibility. It is undocumented and restrictive in the maximum size of JSON |
| 175 | documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, |
| 176 | which can in turn be serialized into the CBOR binary format and vice versa. The |
| 177 | CBOR format is a well-defined and less restrictive binary representation for a |
| 178 | superset of JSON. |
| 179 | |
| 180 | \sa fromBinaryData(), QCborValue |
| 181 | */ |
| 182 | QByteArray toBinaryData(const QJsonDocument &document) |
| 183 | { |
| 184 | int size = 0; |
| 185 | const char *raw = toRawData(document, size: &size); |
| 186 | return QByteArray(raw, size); |
| 187 | } |
| 188 | |
| 189 | } // namespace QBinaryJson |
| 190 | |
| 191 | namespace QBinaryJsonPrivate { |
| 192 | |
| 193 | static Q_CONSTEXPR Base emptyArray = { |
| 194 | .size: { qle_uint(sizeof(Base)) }, |
| 195 | .isObjectAndLength: { 0 }, |
| 196 | .tableOffset: { qle_uint(0) } |
| 197 | }; |
| 198 | |
| 199 | static Q_CONSTEXPR Base emptyObject = { |
| 200 | .size: { qle_uint(sizeof(Base)) }, |
| 201 | .isObjectAndLength: { qToLittleEndian(source: 1U) }, |
| 202 | .tableOffset: { qle_uint(0) } |
| 203 | }; |
| 204 | |
| 205 | void MutableData::compact() |
| 206 | { |
| 207 | static_assert(sizeof(Value) == sizeof(offset)); |
| 208 | |
| 209 | Base *base = header->root(); |
| 210 | int reserve = 0; |
| 211 | if (base->isObject()) { |
| 212 | auto *o = static_cast<Object *>(base); |
| 213 | for (uint i = 0; i < o->length(); ++i) |
| 214 | reserve += o->entryAt(i)->usedStorage(b: o); |
| 215 | } else { |
| 216 | auto *a = static_cast<Array *>(base); |
| 217 | for (uint i = 0; i < a->length(); ++i) |
| 218 | reserve += a->at(i)->usedStorage(b: a); |
| 219 | } |
| 220 | |
| 221 | uint size = sizeof(Base) + reserve + base->length() * sizeof(offset); |
| 222 | uint alloc = sizeof(Header) + size; |
| 223 | auto *h = reinterpret_cast<Header *>(malloc(size: alloc)); |
| 224 | Q_CHECK_PTR(h); |
| 225 | h->tag = QJsonDocument::BinaryFormatTag; |
| 226 | h->version = 1; |
| 227 | Base *b = new (h->root()) Base{}; |
| 228 | b->size = size; |
| 229 | if (header->root()->isObject()) |
| 230 | b->setIsObject(); |
| 231 | else |
| 232 | b->setIsArray(); |
| 233 | b->setLength(base->length()); |
| 234 | b->tableOffset = reserve + sizeof(Array); |
| 235 | |
| 236 | uint offset = sizeof(Base); |
| 237 | if (b->isObject()) { |
| 238 | const auto *o = static_cast<const Object *>(base); |
| 239 | auto *no = static_cast<Object *>(b); |
| 240 | |
| 241 | for (uint i = 0; i < o->length(); ++i) { |
| 242 | no->table()[i] = offset; |
| 243 | |
| 244 | const Entry *e = o->entryAt(i); |
| 245 | Entry *ne = no->entryAt(i); |
| 246 | uint s = e->size(); |
| 247 | memcpy(dest: ne, src: e, n: s); |
| 248 | offset += s; |
| 249 | uint dataSize = e->value.usedStorage(b: o); |
| 250 | if (dataSize) { |
| 251 | memcpy(dest: reinterpret_cast<char *>(no) + offset, src: e->value.data(b: o), n: dataSize); |
| 252 | ne->value.setValue(offset); |
| 253 | offset += dataSize; |
| 254 | } |
| 255 | } |
| 256 | } else { |
| 257 | const auto *a = static_cast<const Array *>(base); |
| 258 | auto *na = static_cast<Array *>(b); |
| 259 | |
| 260 | for (uint i = 0; i < a->length(); ++i) { |
| 261 | const Value *v = a->at(i); |
| 262 | Value *nv = na->at(i); |
| 263 | *nv = *v; |
| 264 | uint dataSize = v->usedStorage(b: a); |
| 265 | if (dataSize) { |
| 266 | memcpy(dest: reinterpret_cast<char *>(na) + offset, src: v->data(b: a), n: dataSize); |
| 267 | nv->setValue(offset); |
| 268 | offset += dataSize; |
| 269 | } |
| 270 | } |
| 271 | } |
| 272 | Q_ASSERT(offset == uint(b->tableOffset)); |
| 273 | |
| 274 | free(ptr: header); |
| 275 | header = h; |
| 276 | this->alloc = alloc; |
| 277 | compactionCounter = 0; |
| 278 | } |
| 279 | |
| 280 | bool ConstData::isValid() const |
| 281 | { |
| 282 | if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1U) |
| 283 | return false; |
| 284 | |
| 285 | const Base *root = header->root(); |
| 286 | const uint maxSize = alloc - sizeof(Header); |
| 287 | return root->isObject() |
| 288 | ? static_cast<const Object *>(root)->isValid(maxSize) |
| 289 | : static_cast<const Array *>(root)->isValid(maxSize); |
| 290 | } |
| 291 | |
| 292 | QJsonDocument ConstData::toJsonDocument() const |
| 293 | { |
| 294 | const Base *root = header->root(); |
| 295 | return root->isObject() |
| 296 | ? QJsonDocument(static_cast<const Object *>(root)->toJsonObject()) |
| 297 | : QJsonDocument(static_cast<const Array *>(root)->toJsonArray()); |
| 298 | } |
| 299 | |
| 300 | uint Base::reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace) |
| 301 | { |
| 302 | Q_ASSERT(posInTable <= length()); |
| 303 | if (size + dataSize >= Value::MaxSize) { |
| 304 | qWarning(msg: "QJson: Document too large to store in data structure %d %d %d" , |
| 305 | uint(size), dataSize, Value::MaxSize); |
| 306 | return 0; |
| 307 | } |
| 308 | |
| 309 | offset off = tableOffset; |
| 310 | // move table to new position |
| 311 | if (replace) { |
| 312 | memmove(dest: reinterpret_cast<char *>(table()) + dataSize, src: table(), n: length() * sizeof(offset)); |
| 313 | } else { |
| 314 | memmove(dest: reinterpret_cast<char *>(table() + posInTable + numItems) + dataSize, |
| 315 | src: table() + posInTable, n: (length() - posInTable) * sizeof(offset)); |
| 316 | memmove(dest: reinterpret_cast<char *>(table()) + dataSize, src: table(), n: posInTable * sizeof(offset)); |
| 317 | } |
| 318 | tableOffset += dataSize; |
| 319 | for (uint i = 0; i < numItems; ++i) |
| 320 | table()[posInTable + i] = off; |
| 321 | size += dataSize; |
| 322 | if (!replace) { |
| 323 | setLength(length() + numItems); |
| 324 | size += numItems * sizeof(offset); |
| 325 | } |
| 326 | return off; |
| 327 | } |
| 328 | |
| 329 | uint Object::indexOf(QStringView key, bool *exists) const |
| 330 | { |
| 331 | uint min = 0; |
| 332 | uint n = length(); |
| 333 | while (n > 0) { |
| 334 | uint half = n >> 1; |
| 335 | uint middle = min + half; |
| 336 | if (*entryAt(i: middle) >= key) { |
| 337 | n = half; |
| 338 | } else { |
| 339 | min = middle + 1; |
| 340 | n -= half + 1; |
| 341 | } |
| 342 | } |
| 343 | if (min < length() && *entryAt(i: min) == key) { |
| 344 | *exists = true; |
| 345 | return min; |
| 346 | } |
| 347 | *exists = false; |
| 348 | return min; |
| 349 | } |
| 350 | |
| 351 | QJsonObject Object::toJsonObject() const |
| 352 | { |
| 353 | QJsonObject object; |
| 354 | for (uint i = 0; i < length(); ++i) { |
| 355 | const Entry *e = entryAt(i); |
| 356 | object.insert(key: e->key(), value: e->value.toJsonValue(b: this)); |
| 357 | } |
| 358 | return object; |
| 359 | } |
| 360 | |
| 361 | bool Object::isValid(uint maxSize) const |
| 362 | { |
| 363 | if (size > maxSize || tableOffset + length() * sizeof(offset) > size) |
| 364 | return false; |
| 365 | |
| 366 | QString lastKey; |
| 367 | for (uint i = 0; i < length(); ++i) { |
| 368 | if (table()[i] + sizeof(Entry) >= tableOffset) |
| 369 | return false; |
| 370 | const Entry *e = entryAt(i); |
| 371 | if (!e->isValid(maxSize: tableOffset - table()[i])) |
| 372 | return false; |
| 373 | const QString key = e->key(); |
| 374 | if (key < lastKey) |
| 375 | return false; |
| 376 | if (!e->value.isValid(b: this)) |
| 377 | return false; |
| 378 | lastKey = key; |
| 379 | } |
| 380 | return true; |
| 381 | } |
| 382 | |
| 383 | QJsonArray Array::toJsonArray() const |
| 384 | { |
| 385 | QJsonArray array; |
| 386 | const offset *values = table(); |
| 387 | for (uint i = 0; i < length(); ++i) |
| 388 | array.append(value: reinterpret_cast<const Value *>(values + i)->toJsonValue(b: this)); |
| 389 | return array; |
| 390 | } |
| 391 | |
| 392 | bool Array::isValid(uint maxSize) const |
| 393 | { |
| 394 | if (size > maxSize || tableOffset + length() * sizeof(offset) > size) |
| 395 | return false; |
| 396 | |
| 397 | const offset *values = table(); |
| 398 | for (uint i = 0; i < length(); ++i) { |
| 399 | if (!reinterpret_cast<const Value *>(values + i)->isValid(b: this)) |
| 400 | return false; |
| 401 | } |
| 402 | return true; |
| 403 | } |
| 404 | |
| 405 | uint Value::usedStorage(const Base *b) const |
| 406 | { |
| 407 | uint s = 0; |
| 408 | switch (type()) { |
| 409 | case QJsonValue::Double: |
| 410 | if (!isLatinOrIntValue()) |
| 411 | s = sizeof(double); |
| 412 | break; |
| 413 | case QJsonValue::String: { |
| 414 | const char *d = data(b); |
| 415 | s = isLatinOrIntValue() |
| 416 | ? (sizeof(ushort) |
| 417 | + qFromLittleEndian(source: *reinterpret_cast<const ushort *>(d))) |
| 418 | : (sizeof(int) |
| 419 | + sizeof(ushort) * qFromLittleEndian(source: *reinterpret_cast<const int *>(d))); |
| 420 | break; |
| 421 | } |
| 422 | case QJsonValue::Array: |
| 423 | case QJsonValue::Object: |
| 424 | s = base(b)->size; |
| 425 | break; |
| 426 | case QJsonValue::Null: |
| 427 | case QJsonValue::Bool: |
| 428 | default: |
| 429 | break; |
| 430 | } |
| 431 | return alignedSize(size: s); |
| 432 | } |
| 433 | |
| 434 | QJsonValue Value::toJsonValue(const Base *b) const |
| 435 | { |
| 436 | switch (type()) { |
| 437 | case QJsonValue::Null: |
| 438 | return QJsonValue(QJsonValue::Null); |
| 439 | case QJsonValue::Bool: |
| 440 | return QJsonValue(toBoolean()); |
| 441 | case QJsonValue::Double: |
| 442 | return QJsonValue(toDouble(b)); |
| 443 | case QJsonValue::String: |
| 444 | return QJsonValue(toString(b)); |
| 445 | case QJsonValue::Array: |
| 446 | return static_cast<const Array *>(base(b))->toJsonArray(); |
| 447 | case QJsonValue::Object: |
| 448 | return static_cast<const Object *>(base(b))->toJsonObject(); |
| 449 | case QJsonValue::Undefined: |
| 450 | return QJsonValue(QJsonValue::Undefined); |
| 451 | } |
| 452 | Q_UNREACHABLE_RETURN(QJsonValue(QJsonValue::Undefined)); |
| 453 | } |
| 454 | |
| 455 | inline bool isValidValueOffset(uint offset, uint tableOffset) |
| 456 | { |
| 457 | return offset >= sizeof(Base) |
| 458 | && offset + sizeof(uint) <= tableOffset; |
| 459 | } |
| 460 | |
| 461 | bool Value::isValid(const Base *b) const |
| 462 | { |
| 463 | switch (type()) { |
| 464 | case QJsonValue::Null: |
| 465 | case QJsonValue::Bool: |
| 466 | return true; |
| 467 | case QJsonValue::Double: |
| 468 | return isLatinOrIntValue() || isValidValueOffset(offset: value(), tableOffset: b->tableOffset); |
| 469 | case QJsonValue::String: |
| 470 | if (!isValidValueOffset(offset: value(), tableOffset: b->tableOffset)) |
| 471 | return false; |
| 472 | if (isLatinOrIntValue()) |
| 473 | return asLatin1String(b).isValid(maxSize: b->tableOffset - value()); |
| 474 | return asString(b).isValid(maxSize: b->tableOffset - value()); |
| 475 | case QJsonValue::Array: |
| 476 | return isValidValueOffset(offset: value(), tableOffset: b->tableOffset) |
| 477 | && static_cast<const Array *>(base(b))->isValid(maxSize: b->tableOffset - value()); |
| 478 | case QJsonValue::Object: |
| 479 | return isValidValueOffset(offset: value(), tableOffset: b->tableOffset) |
| 480 | && static_cast<const Object *>(base(b))->isValid(maxSize: b->tableOffset - value()); |
| 481 | default: |
| 482 | return false; |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | uint Value::requiredStorage(const QBinaryJsonValue &v, bool *compressed) |
| 487 | { |
| 488 | *compressed = false; |
| 489 | switch (v.type()) { |
| 490 | case QJsonValue::Double: |
| 491 | if (QBinaryJsonPrivate::compressedNumber(d: v.toDouble()) != INT_MAX) { |
| 492 | *compressed = true; |
| 493 | return 0; |
| 494 | } |
| 495 | return sizeof(double); |
| 496 | case QJsonValue::String: { |
| 497 | QString s = v.toString(); |
| 498 | *compressed = QBinaryJsonPrivate::useCompressed(s); |
| 499 | return QBinaryJsonPrivate::qStringSize(string: s, compress: *compressed); |
| 500 | } |
| 501 | case QJsonValue::Array: |
| 502 | case QJsonValue::Object: |
| 503 | return v.base ? uint(v.base->size) : sizeof(QBinaryJsonPrivate::Base); |
| 504 | case QJsonValue::Undefined: |
| 505 | case QJsonValue::Null: |
| 506 | case QJsonValue::Bool: |
| 507 | break; |
| 508 | } |
| 509 | return 0; |
| 510 | } |
| 511 | |
| 512 | uint Value::valueToStore(const QBinaryJsonValue &v, uint offset) |
| 513 | { |
| 514 | switch (v.type()) { |
| 515 | case QJsonValue::Undefined: |
| 516 | case QJsonValue::Null: |
| 517 | break; |
| 518 | case QJsonValue::Bool: |
| 519 | return v.toBool(); |
| 520 | case QJsonValue::Double: { |
| 521 | int c = QBinaryJsonPrivate::compressedNumber(d: v.toDouble()); |
| 522 | if (c != INT_MAX) |
| 523 | return c; |
| 524 | } |
| 525 | Q_FALLTHROUGH(); |
| 526 | case QJsonValue::String: |
| 527 | case QJsonValue::Array: |
| 528 | case QJsonValue::Object: |
| 529 | return offset; |
| 530 | } |
| 531 | return 0; |
| 532 | } |
| 533 | |
| 534 | void Value::copyData(const QBinaryJsonValue &v, char *dest, bool compressed) |
| 535 | { |
| 536 | switch (v.type()) { |
| 537 | case QJsonValue::Double: |
| 538 | if (!compressed) |
| 539 | qToLittleEndian(src: v.toDouble(), dest); |
| 540 | break; |
| 541 | case QJsonValue::String: { |
| 542 | const QString str = v.toString(); |
| 543 | QBinaryJsonPrivate::copyString(dest, str, compress: compressed); |
| 544 | break; |
| 545 | } |
| 546 | case QJsonValue::Array: |
| 547 | case QJsonValue::Object: { |
| 548 | const QBinaryJsonPrivate::Base *b = v.base; |
| 549 | if (!b) |
| 550 | b = (v.type() == QJsonValue::Array ? &emptyArray : &emptyObject); |
| 551 | memcpy(dest: dest, src: b, n: b->size); |
| 552 | break; |
| 553 | } |
| 554 | default: |
| 555 | break; |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | } // namespace QBinaryJsonPrivate |
| 560 | |
| 561 | QT_END_NAMESPACE |
| 562 | |