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