1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <cmath>
42#include <qlocale.h>
43#include "qjsonwriter_p.h"
44#include "qjson_p.h"
45#include "private/qutfcodec_p.h"
46#include <private/qnumeric_p.h>
47
48QT_BEGIN_NAMESPACE
49
50using namespace QJsonPrivate;
51
52static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact);
53static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact);
54
55static inline uchar hexdig(uint u)
56{
57 return (u < 0xa ? '0' + u : 'a' + u - 0xa);
58}
59
60static QByteArray escapedString(const QString &s)
61{
62 QByteArray ba(s.length(), Qt::Uninitialized);
63
64 uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
65 const uchar *ba_end = cursor + ba.length();
66 const ushort *src = reinterpret_cast<const ushort *>(s.constBegin());
67 const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd());
68
69 while (src != end) {
70 if (cursor >= ba_end - 6) {
71 // ensure we have enough space
72 int pos = cursor - (const uchar *)ba.constData();
73 ba.resize(ba.size()*2);
74 cursor = (uchar *)ba.data() + pos;
75 ba_end = (const uchar *)ba.constData() + ba.length();
76 }
77
78 uint u = *src++;
79 if (u < 0x80) {
80 if (u < 0x20 || u == 0x22 || u == 0x5c) {
81 *cursor++ = '\\';
82 switch (u) {
83 case 0x22:
84 *cursor++ = '"';
85 break;
86 case 0x5c:
87 *cursor++ = '\\';
88 break;
89 case 0x8:
90 *cursor++ = 'b';
91 break;
92 case 0xc:
93 *cursor++ = 'f';
94 break;
95 case 0xa:
96 *cursor++ = 'n';
97 break;
98 case 0xd:
99 *cursor++ = 'r';
100 break;
101 case 0x9:
102 *cursor++ = 't';
103 break;
104 default:
105 *cursor++ = 'u';
106 *cursor++ = '0';
107 *cursor++ = '0';
108 *cursor++ = hexdig(u>>4);
109 *cursor++ = hexdig(u & 0xf);
110 }
111 } else {
112 *cursor++ = (uchar)u;
113 }
114 } else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) < 0) {
115 // failed to get valid utf8 use JSON escape sequence
116 *cursor++ = '\\';
117 *cursor++ = 'u';
118 *cursor++ = hexdig(u>>12 & 0x0f);
119 *cursor++ = hexdig(u>>8 & 0x0f);
120 *cursor++ = hexdig(u>>4 & 0x0f);
121 *cursor++ = hexdig(u & 0x0f);
122 }
123 }
124
125 ba.resize(cursor - (const uchar *)ba.constData());
126 return ba;
127}
128
129static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &v, QByteArray &json, int indent, bool compact)
130{
131 QJsonValue::Type type = (QJsonValue::Type)(uint)v.type;
132 switch (type) {
133 case QJsonValue::Bool:
134 json += v.toBoolean() ? "true" : "false";
135 break;
136 case QJsonValue::Double: {
137 const double d = v.toDouble(b);
138 if (qIsFinite(d)) { // +2 to format to ensure the expected precision
139 quint64 absInt;
140 json += QByteArray::number(d, convertDoubleTo(std::abs(d), &absInt) ? 'f' : 'g',
141 QLocale::FloatingPointShortest);
142 } else {
143 json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
144 }
145 break;
146 }
147 case QJsonValue::String:
148 json += '"';
149 json += escapedString(v.toString(b));
150 json += '"';
151 break;
152 case QJsonValue::Array:
153 json += compact ? "[" : "[\n";
154 arrayContentToJson(static_cast<QJsonPrivate::Array *>(v.base(b)), json, indent + (compact ? 0 : 1), compact);
155 json += QByteArray(4*indent, ' ');
156 json += ']';
157 break;
158 case QJsonValue::Object:
159 json += compact ? "{" : "{\n";
160 objectContentToJson(static_cast<QJsonPrivate::Object *>(v.base(b)), json, indent + (compact ? 0 : 1), compact);
161 json += QByteArray(4*indent, ' ');
162 json += '}';
163 break;
164 case QJsonValue::Null:
165 default:
166 json += "null";
167 }
168}
169
170static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact)
171{
172 if (!a || !a->length)
173 return;
174
175 QByteArray indentString(4*indent, ' ');
176
177 uint i = 0;
178 while (1) {
179 json += indentString;
180 valueToJson(a, a->at(i), json, indent, compact);
181
182 if (++i == a->length) {
183 if (!compact)
184 json += '\n';
185 break;
186 }
187
188 json += compact ? "," : ",\n";
189 }
190}
191
192
193static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact)
194{
195 if (!o || !o->length)
196 return;
197
198 QByteArray indentString(4*indent, ' ');
199
200 uint i = 0;
201 while (1) {
202 QJsonPrivate::Entry *e = o->entryAt(i);
203 json += indentString;
204 json += '"';
205 json += escapedString(e->key());
206 json += compact ? "\":" : "\": ";
207 valueToJson(o, e->value, json, indent, compact);
208
209 if (++i == o->length) {
210 if (!compact)
211 json += '\n';
212 break;
213 }
214
215 json += compact ? "," : ",\n";
216 }
217}
218
219void Writer::objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact)
220{
221 json.reserve(json.size() + (o ? (int)o->size : 16));
222 json += compact ? "{" : "{\n";
223 objectContentToJson(o, json, indent + (compact ? 0 : 1), compact);
224 json += QByteArray(4*indent, ' ');
225 json += compact ? "}" : "}\n";
226}
227
228void Writer::arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact)
229{
230 json.reserve(json.size() + (a ? (int)a->size : 16));
231 json += compact ? "[" : "[\n";
232 arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact);
233 json += QByteArray(4*indent, ' ');
234 json += compact ? "]" : "]\n";
235}
236
237QT_END_NAMESPACE
238