blob: 4cb7e26efc900ec10ce84a18ed41563b72ca7469 [file] [log] [blame]
José Fonseca17a45412012-11-28 09:44:01 +00001/**************************************************************************
2 *
3 * Copyright 2011 Jose Fonseca
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 **************************************************************************/
25
26/*
27 * JSON writing functions.
28 */
29
30
31#include <assert.h>
32#include <string.h>
33
José Fonseca6083cfc2012-11-28 09:58:09 +000034#include <sstream>
35
José Fonseca17a45412012-11-28 09:44:01 +000036#include "json.hpp"
37
38
39void
40JSONWriter::newline(void) {
41 os << "\n";
42 for (int i = 0; i < level; ++i)
43 os << " ";
44}
45
46void
47JSONWriter::separator(void) {
48 if (value) {
49 os << ",";
50 switch (space) {
51 case '\0':
52 break;
53 case '\n':
54 newline();
55 break;
56 default:
57 os << space;
58 break;
59 }
60 } else {
61 if (space == '\n') {
62 newline();
63 }
64 }
65}
66
67static void
68escapeAsciiString(std::ostream &os, const char *str) {
69 os << "\"";
70
71 const unsigned char *src = (const unsigned char *)str;
72 unsigned char c;
73 while ((c = *src++)) {
74 if ((c == '\"') ||
75 (c == '\\')) {
76 // escape character
77 os << '\\' << (unsigned char)c;
78 } else if ((c >= 0x20 && c <= 0x7e) ||
79 c == '\t' ||
80 c == '\r' ||
81 c == '\n') {
82 // pass-through character
83 os << (unsigned char)c;
84 } else {
85 assert(0);
86 os << "?";
87 }
88 }
89
90 os << "\"";
91}
92
93static void
94escapeUnicodeString(std::ostream &os, const char *str) {
95 os << "\"";
96
97 const char *locale = setlocale(LC_CTYPE, "");
98 const char *src = str;
99 mbstate_t state;
100
101 memset(&state, 0, sizeof state);
102
103 do {
104 // Convert characters one at a time in order to recover from
105 // conversion errors
106 wchar_t c;
107 size_t written = mbsrtowcs(&c, &src, 1, &state);
108 if (written == 0) {
109 // completed
110 break;
111 } if (written == (size_t)-1) {
112 // conversion error -- skip
113 os << "?";
114 do {
115 ++src;
116 } while (*src & 0x80);
117 } else if ((c == '\"') ||
118 (c == '\\')) {
119 // escape character
120 os << '\\' << (unsigned char)c;
121 } else if ((c >= 0x20 && c <= 0x7e) ||
122 c == '\t' ||
123 c == '\r' ||
124 c == '\n') {
125 // pass-through character
126 os << (unsigned char)c;
127 } else {
128 // unicode
José Fonsecadbfe0c42015-03-26 12:23:41 +0000129 os << "\\u" << std::hex << std::setfill('0') << std::setw(4) << (unsigned)c << std::setfill(' ') << std::dec;
José Fonseca17a45412012-11-28 09:44:01 +0000130 }
131 } while (src);
132
133 setlocale(LC_CTYPE, locale);
134
135 os << "\"";
136}
137
138static void
139encodeBase64String(std::ostream &os, const unsigned char *bytes, size_t size) {
140 const char *table64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
141 unsigned char c0, c1, c2, c3;
142 char buf[4];
143 unsigned written;
144
145 os << "\"";
146
147 written = 0;
148 while (size >= 3) {
149 c0 = bytes[0] >> 2;
150 c1 = ((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xf0) >> 4);
151 c2 = ((bytes[1] & 0x0f) << 2) | ((bytes[2] & 0xc0) >> 6);
152 c3 = bytes[2] & 0x3f;
153
154 buf[0] = table64[c0];
155 buf[1] = table64[c1];
156 buf[2] = table64[c2];
157 buf[3] = table64[c3];
158
159 os.write(buf, 4);
160
161 bytes += 3;
162 size -= 3;
163 ++written;
164
165 if (written >= 76/4 && size) {
166 os << "\n";
167 written = 0;
168 }
169 }
170
171 if (size > 0) {
172 c0 = bytes[0] >> 2;
173 c1 = ((bytes[0] & 0x03) << 4);
174 buf[2] = '=';
175 buf[3] = '=';
176
177 if (size > 1) {
178 c1 |= ((bytes[1] & 0xf0) >> 4);
179 c2 = ((bytes[1] & 0x0f) << 2);
180 if (size > 2) {
181 c2 |= ((bytes[2] & 0xc0) >> 6);
182 c3 = bytes[2] & 0x3f;
183 buf[3] = table64[c3];
184 }
185 buf[2] = table64[c2];
186 }
187 buf[1] = table64[c1];
188 buf[0] = table64[c0];
189
190 os.write(buf, 4);
191 }
192
193 os << "\"";
194}
195
196JSONWriter::JSONWriter(std::ostream &_os) :
197 os(_os),
198 level(0),
199 value(false),
200 space(0)
201{
202 beginObject();
203}
204
205JSONWriter::~JSONWriter() {
206 endObject();
207 newline();
208}
209
210void
211JSONWriter::beginObject() {
212 separator();
213 os << "{";
214 ++level;
215 value = false;
216}
217
218void
219JSONWriter::endObject() {
220 --level;
221 if (value)
222 newline();
223 os << "}";
224 value = true;
225 space = '\n';
226}
227
228void
229JSONWriter::beginMember(const char * name) {
230 space = 0;
231 separator();
232 newline();
233 escapeAsciiString(os, name);
234 os << ": ";
235 value = false;
236}
237
238void
239JSONWriter::endMember(void) {
240 assert(value);
241 value = true;
242 space = 0;
243}
244
245void
246JSONWriter::beginArray() {
247 separator();
248 os << "[";
249 ++level;
250 value = false;
251 space = 0;
252}
253
254void
255JSONWriter::endArray(void) {
256 --level;
257 if (space == '\n') {
258 newline();
259 }
260 os << "]";
261 value = true;
262 space = '\n';
263}
264
265void
266JSONWriter::writeString(const char *s) {
267 if (!s) {
268 writeNull();
269 return;
270 }
271
272 separator();
273 escapeUnicodeString(os, s);
274 value = true;
275 space = ' ';
276}
277
278void
279JSONWriter::writeBase64(const void *bytes, size_t size) {
280 separator();
281 encodeBase64String(os, (const unsigned char *)bytes, size);
282 value = true;
283 space = ' ';
284}
285
286void
287JSONWriter::writeNull(void) {
288 separator();
289 os << "null";
290 value = true;
291 space = ' ';
292}
293
294void
295JSONWriter::writeBool(bool b) {
296 separator();
297 os << (b ? "true" : "false");
298 value = true;
299 space = ' ';
300}