1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qbinaryjson_p.h"
41
42#include <qjsonobject.h>
43#include <qjsonarray.h>
44
45QT_BEGIN_NAMESPACE
46
47namespace QBinaryJsonPrivate {
48
49static Q_CONSTEXPR Base emptyArray = {
50 .size: { qle_uint(sizeof(Base)) },
51 { ._dummy: 0 },
52 .tableOffset: { qle_uint(0) }
53};
54
55static Q_CONSTEXPR Base emptyObject = {
56 .size: { qle_uint(sizeof(Base)) },
57 { ._dummy: qToLittleEndian(source: 1U) },
58 .tableOffset: { qle_uint(0) }
59};
60
61void MutableData::compact()
62{
63 Q_STATIC_ASSERT(sizeof(Value) == sizeof(offset));
64
65 Base *base = header->root();
66 int reserve = 0;
67 if (base->is_object) {
68 auto *o = static_cast<Object *>(base);
69 for (uint i = 0; i < o->length; ++i)
70 reserve += o->entryAt(i)->usedStorage(b: o);
71 } else {
72 auto *a = static_cast<Array *>(base);
73 for (uint i = 0; i < a->length; ++i)
74 reserve += a->at(i)->usedStorage(b: a);
75 }
76
77 uint size = sizeof(Base) + reserve + base->length * sizeof(offset);
78 uint alloc = sizeof(Header) + size;
79 auto *h = reinterpret_cast<Header *>(malloc(size: alloc));
80 Q_CHECK_PTR(h);
81 h->tag = QJsonDocument::BinaryFormatTag;
82 h->version = 1;
83 Base *b = h->root();
84 b->size = size;
85 b->is_object = header->root()->is_object;
86 b->length = base->length;
87 b->tableOffset = reserve + sizeof(Array);
88
89 uint offset = sizeof(Base);
90 if (b->is_object) {
91 const auto *o = static_cast<const Object *>(base);
92 auto *no = static_cast<Object *>(b);
93
94 for (uint i = 0; i < o->length; ++i) {
95 no->table()[i] = offset;
96
97 const Entry *e = o->entryAt(i);
98 Entry *ne = no->entryAt(i);
99 uint s = e->size();
100 memcpy(dest: ne, src: e, n: s);
101 offset += s;
102 uint dataSize = e->value.usedStorage(b: o);
103 if (dataSize) {
104 memcpy(dest: reinterpret_cast<char *>(no) + offset, src: e->value.data(b: o), n: dataSize);
105 ne->value.value = offset;
106 offset += dataSize;
107 }
108 }
109 } else {
110 const auto *a = static_cast<const Array *>(base);
111 auto *na = static_cast<Array *>(b);
112
113 for (uint i = 0; i < a->length; ++i) {
114 const Value *v = a->at(i);
115 Value *nv = na->at(i);
116 *nv = *v;
117 uint dataSize = v->usedStorage(b: a);
118 if (dataSize) {
119 memcpy(dest: reinterpret_cast<char *>(na) + offset, src: v->data(b: a), n: dataSize);
120 nv->value = offset;
121 offset += dataSize;
122 }
123 }
124 }
125 Q_ASSERT(offset == uint(b->tableOffset));
126
127 free(ptr: header);
128 header = h;
129 this->alloc = alloc;
130 compactionCounter = 0;
131}
132
133bool ConstData::isValid() const
134{
135 if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1U)
136 return false;
137
138 const Base *root = header->root();
139 const uint maxSize = alloc - sizeof(Header);
140 return root->is_object
141 ? static_cast<const Object *>(root)->isValid(maxSize)
142 : static_cast<const Array *>(root)->isValid(maxSize);
143}
144
145QJsonDocument ConstData::toJsonDocument() const
146{
147 const Base *root = header->root();
148 return root->is_object
149 ? QJsonDocument(static_cast<const Object *>(root)->toJsonObject())
150 : QJsonDocument(static_cast<const Array *>(root)->toJsonArray());
151}
152
153uint Base::reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace)
154{
155 Q_ASSERT(posInTable <= length);
156 if (size + dataSize >= Value::MaxSize) {
157 qWarning(msg: "QJson: Document too large to store in data structure %d %d %d",
158 uint(size), dataSize, Value::MaxSize);
159 return 0;
160 }
161
162 offset off = tableOffset;
163 // move table to new position
164 if (replace) {
165 memmove(dest: reinterpret_cast<char *>(table()) + dataSize, src: table(), n: length * sizeof(offset));
166 } else {
167 memmove(dest: reinterpret_cast<char *>(table() + posInTable + numItems) + dataSize,
168 src: table() + posInTable, n: (length - posInTable) * sizeof(offset));
169 memmove(dest: reinterpret_cast<char *>(table()) + dataSize, src: table(), n: posInTable * sizeof(offset));
170 }
171 tableOffset += dataSize;
172 for (uint i = 0; i < numItems; ++i)
173 table()[posInTable + i] = off;
174 size += dataSize;
175 if (!replace) {
176 length += numItems;
177 size += numItems * sizeof(offset);
178 }
179 return off;
180}
181
182uint Object::indexOf(QStringView key, bool *exists) const
183{
184 uint min = 0;
185 uint n = length;
186 while (n > 0) {
187 uint half = n >> 1;
188 uint middle = min + half;
189 if (*entryAt(i: middle) >= key) {
190 n = half;
191 } else {
192 min = middle + 1;
193 n -= half + 1;
194 }
195 }
196 if (min < length && *entryAt(i: min) == key) {
197 *exists = true;
198 return min;
199 }
200 *exists = false;
201 return min;
202}
203
204QJsonObject Object::toJsonObject() const
205{
206 QJsonObject object;
207 for (uint i = 0; i < length; ++i) {
208 const Entry *e = entryAt(i);
209 object.insert(key: e->key(), value: e->value.toJsonValue(b: this));
210 }
211 return object;
212}
213
214bool Object::isValid(uint maxSize) const
215{
216 if (size > maxSize || tableOffset + length * sizeof(offset) > size)
217 return false;
218
219 QString lastKey;
220 for (uint i = 0; i < length; ++i) {
221 if (table()[i] + sizeof(Entry) >= tableOffset)
222 return false;
223 const Entry *e = entryAt(i);
224 if (!e->isValid(maxSize: tableOffset - table()[i]))
225 return false;
226 const QString key = e->key();
227 if (key < lastKey)
228 return false;
229 if (!e->value.isValid(b: this))
230 return false;
231 lastKey = key;
232 }
233 return true;
234}
235
236QJsonArray Array::toJsonArray() const
237{
238 QJsonArray array;
239 const offset *values = table();
240 for (uint i = 0; i < length; ++i)
241 array.append(value: reinterpret_cast<const Value *>(values + i)->toJsonValue(b: this));
242 return array;
243}
244
245bool Array::isValid(uint maxSize) const
246{
247 if (size > maxSize || tableOffset + length * sizeof(offset) > size)
248 return false;
249
250 const offset *values = table();
251 for (uint i = 0; i < length; ++i) {
252 if (!reinterpret_cast<const Value *>(values + i)->isValid(b: this))
253 return false;
254 }
255 return true;
256}
257
258uint Value::usedStorage(const Base *b) const
259{
260 uint s = 0;
261 switch (type) {
262 case QJsonValue::Double:
263 if (!latinOrIntValue)
264 s = sizeof(double);
265 break;
266 case QJsonValue::String: {
267 const char *d = data(b);
268 s = latinOrIntValue
269 ? (sizeof(ushort)
270 + qFromLittleEndian(source: *reinterpret_cast<const ushort *>(d)))
271 : (sizeof(int)
272 + sizeof(ushort) * qFromLittleEndian(source: *reinterpret_cast<const int *>(d)));
273 break;
274 }
275 case QJsonValue::Array:
276 case QJsonValue::Object:
277 s = base(b)->size;
278 break;
279 case QJsonValue::Null:
280 case QJsonValue::Bool:
281 default:
282 break;
283 }
284 return alignedSize(size: s);
285}
286
287QJsonValue Value::toJsonValue(const Base *b) const
288{
289 switch (type) {
290 case QJsonValue::Null:
291 return QJsonValue(QJsonValue::Null);
292 case QJsonValue::Bool:
293 return QJsonValue(toBoolean());
294 case QJsonValue::Double:
295 return QJsonValue(toDouble(b));
296 case QJsonValue::String:
297 return QJsonValue(toString(b));
298 case QJsonValue::Array:
299 return static_cast<const Array *>(base(b))->toJsonArray();
300 case QJsonValue::Object:
301 return static_cast<const Object *>(base(b))->toJsonObject();
302 case QJsonValue::Undefined:
303 return QJsonValue(QJsonValue::Undefined);
304 }
305 Q_UNREACHABLE();
306 return QJsonValue(QJsonValue::Undefined);
307}
308
309inline bool isValidValueOffset(uint offset, uint tableOffset)
310{
311 return offset >= sizeof(Base)
312 && offset + sizeof(uint) <= tableOffset;
313}
314
315bool Value::isValid(const Base *b) const
316{
317 switch (type) {
318 case QJsonValue::Null:
319 case QJsonValue::Bool:
320 return true;
321 case QJsonValue::Double:
322 return latinOrIntValue || isValidValueOffset(offset: value, tableOffset: b->tableOffset);
323 case QJsonValue::String:
324 if (!isValidValueOffset(offset: value, tableOffset: b->tableOffset))
325 return false;
326 if (latinOrIntValue)
327 return asLatin1String(b).isValid(maxSize: b->tableOffset - value);
328 return asString(b).isValid(maxSize: b->tableOffset - value);
329 case QJsonValue::Array:
330 return isValidValueOffset(offset: value, tableOffset: b->tableOffset)
331 && static_cast<const Array *>(base(b))->isValid(maxSize: b->tableOffset - value);
332 case QJsonValue::Object:
333 return isValidValueOffset(offset: value, tableOffset: b->tableOffset)
334 && static_cast<const Object *>(base(b))->isValid(maxSize: b->tableOffset - value);
335 default:
336 return false;
337 }
338}
339
340uint Value::requiredStorage(const QBinaryJsonValue &v, bool *compressed)
341{
342 *compressed = false;
343 switch (v.type()) {
344 case QJsonValue::Double:
345 if (QBinaryJsonPrivate::compressedNumber(d: v.toDouble()) != INT_MAX) {
346 *compressed = true;
347 return 0;
348 }
349 return sizeof(double);
350 case QJsonValue::String: {
351 QString s = v.toString();
352 *compressed = QBinaryJsonPrivate::useCompressed(s);
353 return QBinaryJsonPrivate::qStringSize(string: s, compress: *compressed);
354 }
355 case QJsonValue::Array:
356 case QJsonValue::Object:
357 return v.base ? uint(v.base->size) : sizeof(QBinaryJsonPrivate::Base);
358 case QJsonValue::Undefined:
359 case QJsonValue::Null:
360 case QJsonValue::Bool:
361 break;
362 }
363 return 0;
364}
365
366uint Value::valueToStore(const QBinaryJsonValue &v, uint offset)
367{
368 switch (v.type()) {
369 case QJsonValue::Undefined:
370 case QJsonValue::Null:
371 break;
372 case QJsonValue::Bool:
373 return v.toBool();
374 case QJsonValue::Double: {
375 int c = QBinaryJsonPrivate::compressedNumber(d: v.toDouble());
376 if (c != INT_MAX)
377 return c;
378 }
379 Q_FALLTHROUGH();
380 case QJsonValue::String:
381 case QJsonValue::Array:
382 case QJsonValue::Object:
383 return offset;
384 }
385 return 0;
386}
387
388void Value::copyData(const QBinaryJsonValue &v, char *dest, bool compressed)
389{
390 switch (v.type()) {
391 case QJsonValue::Double:
392 if (!compressed)
393 qToLittleEndian(src: v.toDouble(), dest);
394 break;
395 case QJsonValue::String: {
396 const QString str = v.toString();
397 QBinaryJsonPrivate::copyString(dest, str, compress: compressed);
398 break;
399 }
400 case QJsonValue::Array:
401 case QJsonValue::Object: {
402 const QBinaryJsonPrivate::Base *b = v.base;
403 if (!b)
404 b = (v.type() == QJsonValue::Array ? &emptyArray : &emptyObject);
405 memcpy(dest: dest, src: b, n: b->size);
406 break;
407 }
408 default:
409 break;
410 }
411}
412
413} // namespace QBinaryJsonPrivate
414
415QT_END_NAMESPACE
416

source code of qtbase/src/corelib/serialization/qbinaryjson.cpp