/**************************************************************************** ** ** 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 #include 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(base); for (uint i = 0; i < o->length; ++i) reserve += o->entryAt(i)->usedStorage(o); } else { auto *a = static_cast(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
(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(base); auto *no = static_cast(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(no) + offset, e->value.data(o), dataSize); ne->value.value = offset; offset += dataSize; } } } else { const auto *a = static_cast(base); auto *na = static_cast(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(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(root)->isValid(maxSize) : static_cast(root)->isValid(maxSize); } QJsonDocument ConstData::toJsonDocument() const { const Base *root = header->root(); return root->is_object ? QJsonDocument(static_cast(root)->toJsonObject()) : QJsonDocument(static_cast(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(table()) + dataSize, table(), length * sizeof(offset)); } else { memmove(reinterpret_cast(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable) * sizeof(offset)); memmove(reinterpret_cast(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(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(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(d))) : (sizeof(int) + sizeof(ushort) * qFromLittleEndian(*reinterpret_cast(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(base(b))->toJsonArray(); case QJsonValue::Object: return static_cast(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(base(b))->isValid(b->tableOffset - value); case QJsonValue::Object: return isValidValueOffset(value, b->tableOffset) && static_cast(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