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 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | namespace QBinaryJsonPrivate { |
48 | |
49 | static Q_CONSTEXPR Base emptyArray = { |
50 | .size: { qle_uint(sizeof(Base)) }, |
51 | { ._dummy: 0 }, |
52 | .tableOffset: { qle_uint(0) } |
53 | }; |
54 | |
55 | static Q_CONSTEXPR Base emptyObject = { |
56 | .size: { qle_uint(sizeof(Base)) }, |
57 | { ._dummy: qToLittleEndian(source: 1U) }, |
58 | .tableOffset: { qle_uint(0) } |
59 | }; |
60 | |
61 | void 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 | |
133 | bool 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 | |
145 | QJsonDocument 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 | |
153 | uint 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 | |
182 | uint 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 | |
204 | QJsonObject 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 | |
214 | bool 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 | |
236 | QJsonArray 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 | |
245 | bool 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 | |
258 | uint 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 | |
287 | QJsonValue 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 | |
309 | inline bool isValidValueOffset(uint offset, uint tableOffset) |
310 | { |
311 | return offset >= sizeof(Base) |
312 | && offset + sizeof(uint) <= tableOffset; |
313 | } |
314 | |
315 | bool 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 | |
340 | uint 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 | |
366 | uint 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 | |
388 | void 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 | |
415 | QT_END_NAMESPACE |
416 | |