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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | using namespace QJsonPrivate; |
16 | |
17 | static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact); |
18 | static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact); |
19 | |
20 | static inline uchar hexdig(uint u) |
21 | { |
22 | return (u < 0xa ? '0' + u : 'a' + u - 0xa); |
23 | } |
24 | |
25 | static 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 | |
96 | static 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 | |
142 | static 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 | |
165 | static 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 | |
191 | void 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 | |
200 | void 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 | |
209 | QT_END_NAMESPACE |
210 | |