blob: bf1309e73ee554fe3586c64a71e11f000cf090b2 [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);
José Fonseca17a45412012-11-28 09:44:01 +0000174
Jose Fonseca32e03d12015-11-02 12:23:22 +0000175 buf[3] = '=';
José Fonseca17a45412012-11-28 09:44:01 +0000176 if (size > 1) {
177 c1 |= ((bytes[1] & 0xf0) >> 4);
178 c2 = ((bytes[1] & 0x0f) << 2);
José Fonseca17a45412012-11-28 09:44:01 +0000179 buf[2] = table64[c2];
Jose Fonseca32e03d12015-11-02 12:23:22 +0000180 } else {
181 buf[2] = '=';
José Fonseca17a45412012-11-28 09:44:01 +0000182 }
183 buf[1] = table64[c1];
184 buf[0] = table64[c0];
185
186 os.write(buf, 4);
187 }
188
189 os << "\"";
190}
191
192JSONWriter::JSONWriter(std::ostream &_os) :
193 os(_os),
194 level(0),
195 value(false),
196 space(0)
197{
198 beginObject();
199}
200
201JSONWriter::~JSONWriter() {
202 endObject();
203 newline();
204}
205
206void
207JSONWriter::beginObject() {
208 separator();
209 os << "{";
210 ++level;
211 value = false;
212}
213
214void
215JSONWriter::endObject() {
216 --level;
217 if (value)
218 newline();
219 os << "}";
220 value = true;
221 space = '\n';
222}
223
224void
225JSONWriter::beginMember(const char * name) {
226 space = 0;
227 separator();
228 newline();
229 escapeAsciiString(os, name);
230 os << ": ";
231 value = false;
232}
233
234void
235JSONWriter::endMember(void) {
236 assert(value);
237 value = true;
238 space = 0;
239}
240
241void
242JSONWriter::beginArray() {
243 separator();
244 os << "[";
245 ++level;
246 value = false;
247 space = 0;
248}
249
250void
251JSONWriter::endArray(void) {
252 --level;
253 if (space == '\n') {
254 newline();
255 }
256 os << "]";
257 value = true;
258 space = '\n';
259}
260
261void
262JSONWriter::writeString(const char *s) {
263 if (!s) {
264 writeNull();
265 return;
266 }
267
268 separator();
269 escapeUnicodeString(os, s);
270 value = true;
271 space = ' ';
272}
273
274void
275JSONWriter::writeBase64(const void *bytes, size_t size) {
276 separator();
277 encodeBase64String(os, (const unsigned char *)bytes, size);
278 value = true;
279 space = ' ';
280}
281
282void
283JSONWriter::writeNull(void) {
284 separator();
285 os << "null";
286 value = true;
287 space = ' ';
288}
289
290void
291JSONWriter::writeBool(bool b) {
292 separator();
293 os << (b ? "true" : "false");
294 value = true;
295 space = ' ';
296}