1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <cmath>
6#include <qlocale.h>
7#include "qjsonwriter_p.h"
8#include "qjson_p.h"
9#include "private/qstringconverter_p.h"
10#include <private/qnumeric_p.h>
11#include <private/qcborvalue_p.h>
12
13QT_BEGIN_NAMESPACE
14
15using namespace QJsonPrivate;
16
17static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact);
18static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact);
19
20static inline uchar hexdig(uint u)
21{
22 return (u < 0xa ? '0' + u : 'a' + u - 0xa);
23}
24
25static QByteArray escapedString(QStringView s)
26{
27 // give it a minimum size to ensure the resize() below always adds enough space
28 QByteArray ba(qMax(a: s.size(), b: 16), Qt::Uninitialized);
29
30 auto ba_const_start = [&]() { return reinterpret_cast<const uchar *>(ba.constData()); };
31 uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
32 const uchar *ba_end = cursor + ba.size();
33 const char16_t *src = s.utf16();
34 const char16_t *const end = s.utf16() + s.size();
35
36 while (src != end) {
37 if (cursor >= ba_end - 6) {
38 // ensure we have enough space
39 qptrdiff pos = cursor - ba_const_start();
40 ba.resize(size: ba.size()*2);
41 cursor = reinterpret_cast<uchar *>(ba.data()) + pos;
42 ba_end = ba_const_start() + ba.size();
43 }
44
45 char16_t u = *src++;
46 if (u < 0x80) {
47 if (u < 0x20 || u == 0x22 || u == 0x5c) {
48 *cursor++ = '\\';
49 switch (u) {
50 case 0x22:
51 *cursor++ = '"';
52 break;
53 case 0x5c:
54 *cursor++ = '\\';
55 break;
56 case 0x8:
57 *cursor++ = 'b';
58 break;
59 case 0xc:
60 *cursor++ = 'f';
61 break;
62 case 0xa:
63 *cursor++ = 'n';
64 break;
65 case 0xd:
66 *cursor++ = 'r';
67 break;
68 case 0x9:
69 *cursor++ = 't';
70 break;
71 default:
72 *cursor++ = 'u';
73 *cursor++ = '0';
74 *cursor++ = '0';
75 *cursor++ = hexdig(u: u>>4);
76 *cursor++ = hexdig(u: u & 0xf);
77 }
78 } else {
79 *cursor++ = (uchar)u;
80 }
81 } else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, dst&: cursor, src, end) < 0) {
82 // failed to get valid utf8 use JSON escape sequence
83 *cursor++ = '\\';
84 *cursor++ = 'u';
85 *cursor++ = hexdig(u: u>>12 & 0x0f);
86 *cursor++ = hexdig(u: u>>8 & 0x0f);
87 *cursor++ = hexdig(u: u>>4 & 0x0f);
88 *cursor++ = hexdig(u: u & 0x0f);
89 }
90 }
91
92 ba.resize(size: cursor - ba_const_start());
93 return ba;
94}
95
96static void valueToJson(const QCborValue &v, QByteArray &json, int indent, bool compact)
97{
98 QCborValue::Type type = v.type();
99 switch (type) {
100 case QCborValue::True:
101 json += "true";
102 break;
103 case QCborValue::False:
104 json += "false";
105 break;
106 case QCborValue::Integer:
107 json += QByteArray::number(v.toInteger());
108 break;
109 case QCborValue::Double: {
110 const double d = v.toDouble();
111 if (qIsFinite(d))
112 json += QByteArray::number(d, format: 'g', precision: QLocale::FloatingPointShortest);
113 else
114 json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
115 break;
116 }
117 case QCborValue::String:
118 json += '"';
119 json += escapedString(s: v.toString());
120 json += '"';
121 break;
122 case QCborValue::Array:
123 json += compact ? "[" : "[\n";
124 arrayContentToJson(
125 a: QJsonPrivate::Value::container(v), json, indent: indent + (compact ? 0 : 1), compact);
126 json += QByteArray(4*indent, ' ');
127 json += ']';
128 break;
129 case QCborValue::Map:
130 json += compact ? "{" : "{\n";
131 objectContentToJson(
132 o: QJsonPrivate::Value::container(v), json, indent: indent + (compact ? 0 : 1), compact);
133 json += QByteArray(4*indent, ' ');
134 json += '}';
135 break;
136 case QCborValue::Null:
137 default:
138 json += "null";
139 }
140}
141
142static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact)
143{
144 if (!a || a->elements.empty())
145 return;
146
147 QByteArray indentString(4*indent, ' ');
148
149 qsizetype i = 0;
150 while (true) {
151 json += indentString;
152 valueToJson(v: a->valueAt(idx: i), json, indent, compact);
153
154 if (++i == a->elements.size()) {
155 if (!compact)
156 json += '\n';
157 break;
158 }
159
160 json += compact ? "," : ",\n";
161 }
162}
163
164
165static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact)
166{
167 if (!o || o->elements.empty())
168 return;
169
170 QByteArray indentString(4*indent, ' ');
171
172 qsizetype i = 0;
173 while (true) {
174 QCborValue e = o->valueAt(idx: i);
175 json += indentString;
176 json += '"';
177 json += escapedString(s: o->valueAt(idx: i).toString());
178 json += compact ? "\":" : "\": ";
179 valueToJson(v: o->valueAt(idx: i + 1), json, indent, compact);
180
181 if ((i += 2) == o->elements.size()) {
182 if (!compact)
183 json += '\n';
184 break;
185 }
186
187 json += compact ? "," : ",\n";
188 }
189}
190
191void Writer::objectToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact)
192{
193 json.reserve(asize: json.size() + (o ? (int)o->elements.size() : 16));
194 json += compact ? "{" : "{\n";
195 objectContentToJson(o, json, indent: indent + (compact ? 0 : 1), compact);
196 json += QByteArray(4*indent, ' ');
197 json += compact ? "}" : "}\n";
198}
199
200void Writer::arrayToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact)
201{
202 json.reserve(asize: json.size() + (a ? (int)a->elements.size() : 16));
203 json += compact ? "[" : "[\n";
204 arrayContentToJson(a, json, indent: indent + (compact ? 0 : 1), compact);
205 json += QByteArray(4*indent, ' ');
206 json += compact ? "]" : "]\n";
207}
208
209QT_END_NAMESPACE
210

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