/**************************************************************************** ** ** 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 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(base); for (int i = 0; i < (int)o->length; ++i) reserve += o->entryAt(i)->usedStorage(o); } else { Array *a = static_cast(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); 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(base); Object *no = static_cast(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(base); Array *na = static_cast(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(root)->isValid(maxSize); else res = static_cast(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(const QString &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 ==(const QString &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() == key; } 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(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; } } /*! \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(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