1// Copyright (C) 2020 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QCBORVALUE_P_H
5#define QCBORVALUE_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API.
12// This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qcborvalue.h"
19
20#if QT_CONFIG(cborstreamreader)
21# include "qcborstreamreader.h"
22#endif
23
24#include <private/qglobal_p.h>
25#include <private/qstringconverter_p.h>
26
27#include <math.h>
28
29QT_BEGIN_NAMESPACE
30
31namespace QtCbor {
32struct Undefined {};
33struct Element
34{
35 enum ValueFlag : quint32 {
36 IsContainer = 0x0001,
37 HasByteData = 0x0002,
38 StringIsUtf16 = 0x0004,
39 StringIsAscii = 0x0008
40 };
41 Q_DECLARE_FLAGS(ValueFlags, ValueFlag)
42
43 union {
44 qint64 value;
45 QCborContainerPrivate *container;
46 };
47 QCborValue::Type type;
48 ValueFlags flags = {};
49
50 Element(qint64 v = 0, QCborValue::Type t = QCborValue::Undefined, ValueFlags f = {})
51 : value(v), type(t), flags(f)
52 {}
53
54 Element(QCborContainerPrivate *d, QCborValue::Type t, ValueFlags f = {})
55 : container(d), type(t), flags(f | IsContainer)
56 {}
57
58 double fpvalue() const
59 {
60 double d;
61 memcpy(dest: &d, src: &value, n: sizeof(d));
62 return d;
63 }
64};
65Q_DECLARE_OPERATORS_FOR_FLAGS(Element::ValueFlags)
66static_assert(sizeof(Element) == 16);
67
68struct ByteData
69{
70 QByteArray::size_type len;
71
72 const char *byte() const { return reinterpret_cast<const char *>(this + 1); }
73 char *byte() { return reinterpret_cast<char *>(this + 1); }
74 const QChar *utf16() const { return reinterpret_cast<const QChar *>(this + 1); }
75 QChar *utf16() { return reinterpret_cast<QChar *>(this + 1); }
76
77 QByteArray toByteArray() const { return QByteArray(byte(), len); }
78 QString toString() const { return QString(utf16(), len / 2); }
79 QString toUtf8String() const { return QString::fromUtf8(utf8: byte(), size: len); }
80
81 QByteArray asByteArrayView() const { return QByteArray::fromRawData(data: byte(), size: len); }
82 QLatin1StringView asLatin1() const { return {byte(), len}; }
83 QUtf8StringView asUtf8StringView() const { return QUtf8StringView(byte(), len); }
84 QStringView asStringView() const{ return QStringView(utf16(), len / 2); }
85 QString asQStringRaw() const { return QString::fromRawData(utf16(), size: len / 2); }
86};
87static_assert(std::is_trivial<ByteData>::value);
88static_assert(std::is_standard_layout<ByteData>::value);
89} // namespace QtCbor
90
91Q_DECLARE_TYPEINFO(QtCbor::Element, Q_PRIMITIVE_TYPE);
92
93class QCborContainerPrivate : public QSharedData
94{
95 friend class QExplicitlySharedDataPointer<QCborContainerPrivate>;
96 ~QCborContainerPrivate();
97
98public:
99 enum ContainerDisposition { CopyContainer, MoveContainer };
100
101 QByteArray::size_type usedData = 0;
102 QByteArray data;
103 QList<QtCbor::Element> elements;
104
105 void deref() { if (!ref.deref()) delete this; }
106 void compact(qsizetype reserved);
107 static QCborContainerPrivate *clone(QCborContainerPrivate *d, qsizetype reserved = -1);
108 static QCborContainerPrivate *detach(QCborContainerPrivate *d, qsizetype reserved);
109 static QCborContainerPrivate *grow(QCborContainerPrivate *d, qsizetype index);
110
111 qptrdiff addByteData(const char *block, qsizetype len)
112 {
113 // This function does not do overflow checking, since the len parameter
114 // is expected to be trusted. There's another version of this function
115 // in decodeStringFromCbor(), which checks.
116
117 qptrdiff offset = data.size();
118
119 // align offset
120 offset += alignof(QtCbor::ByteData) - 1;
121 offset &= ~(alignof(QtCbor::ByteData) - 1);
122
123 qptrdiff increment = qptrdiff(sizeof(QtCbor::ByteData)) + len;
124
125 usedData += increment;
126 data.resize(size: offset + increment);
127
128 char *ptr = data.begin() + offset;
129 auto b = new (ptr) QtCbor::ByteData;
130 b->len = len;
131 if (block)
132 memcpy(dest: b->byte(), src: block, n: len);
133
134 return offset;
135 }
136
137 const QtCbor::ByteData *byteData(QtCbor::Element e) const
138 {
139 if ((e.flags & QtCbor::Element::HasByteData) == 0)
140 return nullptr;
141
142 size_t offset = size_t(e.value);
143 Q_ASSERT((offset % alignof(QtCbor::ByteData)) == 0);
144 Q_ASSERT(offset + sizeof(QtCbor::ByteData) <= size_t(data.size()));
145
146 auto b = reinterpret_cast<const QtCbor::ByteData *>(data.constData() + offset);
147 Q_ASSERT(offset + sizeof(*b) + size_t(b->len) <= size_t(data.size()));
148 return b;
149 }
150 const QtCbor::ByteData *byteData(qsizetype idx) const
151 {
152 return byteData(e: elements.at(i: idx));
153 }
154
155 QCborContainerPrivate *containerAt(qsizetype idx, QCborValue::Type type) const
156 {
157 const QtCbor::Element &e = elements.at(i: idx);
158 if (e.type != type || (e.flags & QtCbor::Element::IsContainer) == 0)
159 return nullptr;
160 return e.container;
161 }
162
163 void replaceAt_complex(QtCbor::Element &e, const QCborValue &value, ContainerDisposition disp);
164 void replaceAt_internal(QtCbor::Element &e, const QCborValue &value, ContainerDisposition disp)
165 {
166 if (value.container)
167 return replaceAt_complex(e, value, disp);
168
169 e = { value.value_helper(), value.type() };
170 if (value.isContainer())
171 e.container = nullptr;
172 }
173 void replaceAt(qsizetype idx, const QCborValue &value, ContainerDisposition disp = CopyContainer)
174 {
175 QtCbor::Element &e = elements[idx];
176 if (e.flags & QtCbor::Element::IsContainer) {
177 e.container->deref();
178 e.container = nullptr;
179 e.flags = {};
180 } else if (auto b = byteData(e)) {
181 usedData -= b->len + sizeof(QtCbor::ByteData);
182 }
183 replaceAt_internal(e, value, disp);
184 }
185 void insertAt(qsizetype idx, const QCborValue &value, ContainerDisposition disp = CopyContainer)
186 {
187 replaceAt_internal(e&: *elements.insert(before: elements.begin() + int(idx), t: {}), value, disp);
188 }
189
190 void append(QtCbor::Undefined)
191 {
192 elements.append(t: QtCbor::Element());
193 }
194 void append(qint64 value)
195 {
196 elements.append(t: QtCbor::Element(value , QCborValue::Integer));
197 }
198 void append(QCborTag tag)
199 {
200 elements.append(t: QtCbor::Element(qint64(tag), QCborValue::Tag));
201 }
202 void appendByteData(const char *data, qsizetype len, QCborValue::Type type,
203 QtCbor::Element::ValueFlags extraFlags = {})
204 {
205 elements.append(t: QtCbor::Element(addByteData(block: data, len), type,
206 QtCbor::Element::HasByteData | extraFlags));
207 }
208 void appendAsciiString(const QString &s);
209 void appendAsciiString(const char *str, qsizetype len)
210 {
211 appendByteData(data: str, len, type: QCborValue::String, extraFlags: QtCbor::Element::StringIsAscii);
212 }
213 void appendUtf8String(const char *str, qsizetype len)
214 {
215 appendByteData(data: str, len, type: QCborValue::String);
216 }
217 void append(QLatin1StringView s)
218 {
219 if (!QtPrivate::isAscii(s))
220 return append(s: QString(s));
221
222 // US-ASCII is a subset of UTF-8, so we can keep in 8-bit
223 appendByteData(data: s.latin1(), len: s.size(), type: QCborValue::String,
224 extraFlags: QtCbor::Element::StringIsAscii);
225 }
226 void appendAsciiString(QStringView s);
227
228 void append(const QString &s)
229 {
230 append(s: qToStringViewIgnoringNull(s));
231 }
232
233 void append(QStringView s)
234 {
235 if (QtPrivate::isAscii(s))
236 appendAsciiString(s);
237 else
238 appendByteData(data: reinterpret_cast<const char *>(s.utf16()), len: s.size() * 2,
239 type: QCborValue::String, extraFlags: QtCbor::Element::StringIsUtf16);
240 }
241 void append(const QCborValue &v)
242 {
243 insertAt(idx: elements.size(), value: v);
244 }
245
246 QByteArray byteArrayAt(qsizetype idx) const
247 {
248 const auto &e = elements.at(i: idx);
249 const auto data = byteData(e);
250 if (!data)
251 return QByteArray();
252 return data->toByteArray();
253 }
254 QString stringAt(qsizetype idx) const
255 {
256 const auto &e = elements.at(i: idx);
257 const auto data = byteData(e);
258 if (!data)
259 return QString();
260 if (e.flags & QtCbor::Element::StringIsUtf16)
261 return data->toString();
262 if (e.flags & QtCbor::Element::StringIsAscii)
263 return data->asLatin1();
264 return data->toUtf8String();
265 }
266
267 static void resetValue(QCborValue &v)
268 {
269 v.container = nullptr;
270 }
271
272 static QCborValue makeValue(QCborValue::Type type, qint64 n, QCborContainerPrivate *d = nullptr,
273 ContainerDisposition disp = CopyContainer)
274 {
275 QCborValue result(type);
276 result.n = n;
277 result.container = d;
278 if (d && disp == CopyContainer)
279 d->ref.ref();
280 return result;
281 }
282
283 QCborValue valueAt(qsizetype idx) const
284 {
285 const auto &e = elements.at(i: idx);
286
287 if (e.flags & QtCbor::Element::IsContainer) {
288 if (e.type == QCborValue::Tag && e.container->elements.size() != 2) {
289 // invalid tags can be created due to incomplete parsing
290 return makeValue(type: QCborValue::Invalid, n: 0, d: nullptr);
291 }
292 return makeValue(type: e.type, n: -1, d: e.container);
293 } else if (e.flags & QtCbor::Element::HasByteData) {
294 return makeValue(type: e.type, n: idx, d: const_cast<QCborContainerPrivate *>(this));
295 }
296 return makeValue(type: e.type, n: e.value);
297 }
298 QCborValue extractAt_complex(QtCbor::Element e);
299 QCborValue extractAt(qsizetype idx)
300 {
301 QtCbor::Element e;
302 qSwap(value1&: e, value2&: elements[idx]);
303
304 if (e.flags & QtCbor::Element::IsContainer) {
305 if (e.type == QCborValue::Tag && e.container->elements.size() != 2) {
306 // invalid tags can be created due to incomplete parsing
307 e.container->deref();
308 return makeValue(type: QCborValue::Invalid, n: 0, d: nullptr);
309 }
310 return makeValue(type: e.type, n: -1, d: e.container, disp: MoveContainer);
311 } else if (e.flags & QtCbor::Element::HasByteData) {
312 return extractAt_complex(e);
313 }
314 return makeValue(type: e.type, n: e.value);
315 }
316
317 static QtCbor::Element elementFromValue(const QCborValue &value)
318 {
319 if (value.n >= 0 && value.container)
320 return value.container->elements.at(i: value.n);
321
322 QtCbor::Element e;
323 e.value = value.n;
324 e.type = value.t;
325 if (value.container) {
326 e.container = value.container;
327 e.flags = QtCbor::Element::IsContainer;
328 }
329 return e;
330 }
331
332 static int compareUtf8(const QtCbor::ByteData *b, QLatin1StringView s)
333 {
334 return QUtf8::compareUtf8(utf8: QByteArrayView(b->byte(), b->len), s);
335 }
336
337 static int compareUtf8(const QtCbor::ByteData *b, QStringView s)
338 {
339 return QUtf8::compareUtf8(utf8: QByteArrayView(b->byte(), b->len), utf16: s);
340 }
341
342 template<typename String>
343 int stringCompareElement(const QtCbor::Element &e, String s) const
344 {
345 if (e.type != QCborValue::String)
346 return int(e.type) - int(QCborValue::String);
347
348 const QtCbor::ByteData *b = byteData(e);
349 if (!b)
350 return s.isEmpty() ? 0 : -1;
351
352 if (e.flags & QtCbor::Element::StringIsUtf16)
353 return QtPrivate::compareStrings(b->asStringView(), s);
354 return compareUtf8(b, s);
355 }
356
357 template<typename String>
358 bool stringEqualsElement(const QtCbor::Element &e, String s) const
359 {
360 return stringCompareElement(e, s) == 0;
361 }
362
363 template<typename String>
364 bool stringEqualsElement(qsizetype idx, String s) const
365 {
366 return stringEqualsElement(elements.at(i: idx), s);
367 }
368
369 static int compareElement_helper(const QCborContainerPrivate *c1, QtCbor::Element e1,
370 const QCborContainerPrivate *c2, QtCbor::Element e2);
371 int compareElement(qsizetype idx, const QCborValue &value) const
372 {
373 auto &e1 = elements.at(i: idx);
374 auto e2 = elementFromValue(value);
375 return compareElement_helper(c1: this, e1, c2: value.container, e2);
376 }
377
378 void removeAt(qsizetype idx)
379 {
380 replaceAt(idx, value: {});
381 elements.remove(i: idx);
382 }
383
384 // doesn't apply to JSON
385 template <typename KeyType> QCborValueConstRef findCborMapKey(KeyType key)
386 {
387 qsizetype i = 0;
388 for ( ; i < elements.size(); i += 2) {
389 const auto &e = elements.at(i);
390 bool equals;
391 if constexpr (std::is_same_v<std::decay_t<KeyType>, QCborValue>) {
392 equals = (compareElement(idx: i, value: key) == 0);
393 } else if constexpr (std::is_integral_v<KeyType>) {
394 equals = (e.type == QCborValue::Integer && e.value == key);
395 } else {
396 // assume it's a string
397 equals = stringEqualsElement(i, key);
398 }
399 if (equals)
400 break;
401 }
402 return { this, i + 1 };
403 }
404 template <typename KeyType> static QCborValue findCborMapKey(const QCborValue &self, KeyType key)
405 {
406 if (self.isMap() && self.container) {
407 qsizetype idx = self.container->findCborMapKey(key).i;
408 if (idx < self.container->elements.size())
409 return self.container->valueAt(idx);
410 }
411 return QCborValue();
412 }
413 template <typename KeyType> static QCborValueRef
414 findOrAddMapKey(QCborContainerPrivate *container, KeyType key)
415 {
416 qsizetype size = 0;
417 qsizetype index = size + 1;
418 if (container) {
419 size = container->elements.size();
420 index = container->findCborMapKey<KeyType>(key).i; // returns size + 1 if not found
421 }
422 Q_ASSERT(index & 1);
423 Q_ASSERT((size & 1) == 0);
424
425 container = detach(d: container, reserved: qMax(a: index + 1, b: size));
426 Q_ASSERT(container);
427 Q_ASSERT((container->elements.size() & 1) == 0);
428
429 if (index >= size) {
430 container->append(key);
431 container->append(v: QCborValue());
432 }
433 Q_ASSERT(index < container->elements.size());
434 return { container, index };
435 }
436 template <typename KeyType> static QCborValueRef findOrAddMapKey(QCborMap &map, KeyType key);
437 template <typename KeyType> static QCborValueRef findOrAddMapKey(QCborValue &self, KeyType key);
438 template <typename KeyType> static QCborValueRef findOrAddMapKey(QCborValueRef self, KeyType key);
439
440#if QT_CONFIG(cborstreamreader)
441 void decodeValueFromCbor(QCborStreamReader &reader, int remainingStackDepth);
442 void decodeStringFromCbor(QCborStreamReader &reader);
443 static inline void setErrorInReader(QCborStreamReader &reader, QCborError error);
444#endif
445};
446
447QT_END_NAMESPACE
448
449#endif // QCBORVALUE_P_H
450

source code of qtbase/src/corelib/serialization/qcborvalue_p.h