blob: 0431cf775189d4a6a85fc68d8373a431cd88ac4c [file] [log] [blame]
José Fonseca589082d2011-03-30 09:10:40 +01001/**************************************************************************
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 * Trace writing functions.
28 */
29
30#ifndef _JSON_HPP_
31#define _JSON_HPP_
32
33#include <assert.h>
34#include <stddef.h>
José Fonseca702c1c62011-04-08 07:58:05 +010035#include <wchar.h>
José Fonseca589082d2011-03-30 09:10:40 +010036
Carl Worth112e7472012-07-30 15:01:15 -070037#ifdef _MSC_VER
38# include <float.h>
39# define isfinite _finite
José Fonsecac3912652012-10-26 23:45:35 +010040# define isnan _isnan
Carl Worth112e7472012-07-30 15:01:15 -070041#else
José Fonsecac3912652012-10-26 23:45:35 +010042# include <math.h> // isfinite, isnan
Carl Worth112e7472012-07-30 15:01:15 -070043#endif
44
José Fonseca702c1c62011-04-08 07:58:05 +010045#include <iomanip>
José Fonseca5f2245e2012-05-14 20:20:44 +010046#include <limits>
José Fonsecaa15b7fe2011-07-16 21:08:16 -070047#include <ostream>
48#include <string>
José Fonseca589082d2011-03-30 09:10:40 +010049
50
51class JSONWriter
52{
53private:
54 std::ostream &os;
55
56 int level;
57 bool value;
José Fonsecafc92b762011-04-08 09:44:26 +010058 char space;
José Fonseca589082d2011-03-30 09:10:40 +010059
60 void newline(void) {
61 os << "\n";
62 for (int i = 0; i < level; ++i)
63 os << " ";
64 }
65
66 void separator(void) {
67 if (value) {
68 os << ",";
José Fonsecafc92b762011-04-08 09:44:26 +010069 switch (space) {
70 case '\0':
71 break;
72 case '\n':
73 newline();
74 break;
75 default:
76 os << space;
77 break;
78 }
José Fonseca2e8febd2011-04-09 00:10:30 +010079 } else {
80 if (space == '\n') {
81 newline();
82 }
José Fonseca589082d2011-03-30 09:10:40 +010083 }
84 }
85
José Fonseca702c1c62011-04-08 07:58:05 +010086 void escapeAsciiString(const char *str) {
José Fonseca589082d2011-03-30 09:10:40 +010087 os << "\"";
José Fonseca702c1c62011-04-08 07:58:05 +010088
89 const unsigned char *src = (const unsigned char *)str;
José Fonseca589082d2011-03-30 09:10:40 +010090 unsigned char c;
José Fonseca702c1c62011-04-08 07:58:05 +010091 while ((c = *src++)) {
92 if ((c == '\"') ||
93 (c == '\\')) {
94 // escape character
95 os << '\\' << (unsigned char)c;
96 } else if ((c >= 0x20 && c <= 0x7e) ||
97 c == '\t' ||
98 c == '\r' ||
99 c == '\n') {
100 // pass-through character
101 os << (unsigned char)c;
102 } else {
103 assert(0);
104 os << "?";
José Fonseca589082d2011-03-30 09:10:40 +0100105 }
106 }
José Fonseca702c1c62011-04-08 07:58:05 +0100107
108 os << "\"";
109 }
110
111 void escapeUnicodeString(const char *str) {
112 os << "\"";
113
114 const char *locale = setlocale(LC_CTYPE, "");
115 const char *src = str;
116 mbstate_t state;
117
118 memset(&state, 0, sizeof state);
119
120 do {
121 // Convert characters one at a time in order to recover from
122 // conversion errors
123 wchar_t c;
124 size_t written = mbsrtowcs(&c, &src, 1, &state);
125 if (written == 0) {
126 // completed
127 break;
128 } if (written == (size_t)-1) {
129 // conversion error -- skip
130 os << "?";
131 do {
132 ++src;
133 } while (*src & 0x80);
134 } else if ((c == '\"') ||
135 (c == '\\')) {
136 // escape character
137 os << '\\' << (unsigned char)c;
138 } else if ((c >= 0x20 && c <= 0x7e) ||
139 c == '\t' ||
140 c == '\r' ||
141 c == '\n') {
142 // pass-through character
143 os << (unsigned char)c;
144 } else {
145 // unicode
146 os << "\\u" << std::setfill('0') << std::hex << std::setw(4) << (unsigned)c;
147 os << std::dec;
148 }
149 } while (src);
150
151 setlocale(LC_CTYPE, locale);
152
José Fonseca589082d2011-03-30 09:10:40 +0100153 os << "\"";
154 }
155
José Fonseca23a17d62011-04-09 12:22:58 +0100156 void encodeBase64String(const unsigned char *bytes, size_t size) {
157 const char *table64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
158 unsigned char c0, c1, c2, c3;
159 char buf[4];
160 unsigned written;
161
162 os << "\"";
163
164 written = 0;
165 while (size >= 3) {
166 c0 = bytes[0] >> 2;
167 c1 = ((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xf0) >> 4);
168 c2 = ((bytes[1] & 0x0f) << 2) | ((bytes[2] & 0xc0) >> 6);
169 c3 = bytes[2] & 0x3f;
170
171 buf[0] = table64[c0];
172 buf[1] = table64[c1];
173 buf[2] = table64[c2];
174 buf[3] = table64[c3];
175
176 os.write(buf, 4);
177
178 bytes += 3;
179 size -= 3;
180 ++written;
181
José Fonseca56478ca2011-04-09 12:38:10 +0100182 if (written >= 76/4 && size) {
José Fonseca23a17d62011-04-09 12:22:58 +0100183 os << "\n";
184 written = 0;
185 }
186 }
187
188 if (size > 0) {
189 c0 = bytes[0] >> 2;
190 c1 = ((bytes[0] & 0x03) << 4);
José Fonseca56478ca2011-04-09 12:38:10 +0100191 buf[2] = '=';
192 buf[3] = '=';
José Fonseca23a17d62011-04-09 12:22:58 +0100193
194 if (size > 1) {
195 c1 |= ((bytes[1] & 0xf0) >> 4);
196 c2 = ((bytes[1] & 0x0f) << 2);
197 if (size > 2) {
198 c2 |= ((bytes[2] & 0xc0) >> 6);
199 c3 = bytes[2] & 0x3f;
200 buf[3] = table64[c3];
José Fonseca23a17d62011-04-09 12:22:58 +0100201 }
202 buf[2] = table64[c2];
José Fonseca23a17d62011-04-09 12:22:58 +0100203 }
204 buf[1] = table64[c1];
205 buf[0] = table64[c0];
206
207 os.write(buf, 4);
208 }
209
210 os << "\"";
211 }
212
José Fonseca589082d2011-03-30 09:10:40 +0100213public:
214 JSONWriter(std::ostream &_os) :
215 os(_os),
216 level(0),
José Fonsecafc92b762011-04-08 09:44:26 +0100217 value(false),
218 space(0)
José Fonseca589082d2011-03-30 09:10:40 +0100219 {
220 beginObject();
221 }
222
223 ~JSONWriter() {
224 endObject();
225 newline();
226 }
227
228 inline void beginObject() {
José Fonseca3b4677e2011-04-07 10:14:02 +0100229 separator();
José Fonseca589082d2011-03-30 09:10:40 +0100230 os << "{";
231 ++level;
232 value = false;
233 }
234
235 inline void endObject() {
236 --level;
237 if (value)
238 newline();
239 os << "}";
240 value = true;
José Fonsecafc92b762011-04-08 09:44:26 +0100241 space = '\n';
José Fonseca589082d2011-03-30 09:10:40 +0100242 }
243
244 inline void beginMember(const char * name) {
José Fonsecafc92b762011-04-08 09:44:26 +0100245 space = 0;
José Fonseca589082d2011-03-30 09:10:40 +0100246 separator();
247 newline();
José Fonseca702c1c62011-04-08 07:58:05 +0100248 escapeAsciiString(name);
José Fonseca589082d2011-03-30 09:10:40 +0100249 os << ": ";
250 value = false;
251 }
252
José Fonsecaa15b7fe2011-07-16 21:08:16 -0700253 inline void beginMember(const std::string &name) {
254 beginMember(name.c_str());
255 }
256
José Fonseca589082d2011-03-30 09:10:40 +0100257 inline void endMember(void) {
258 assert(value);
259 value = true;
José Fonsecafc92b762011-04-08 09:44:26 +0100260 space = 0;
José Fonseca589082d2011-03-30 09:10:40 +0100261 }
262
263 inline void beginArray() {
264 separator();
265 os << "[";
José Fonsecafc92b762011-04-08 09:44:26 +0100266 ++level;
José Fonseca589082d2011-03-30 09:10:40 +0100267 value = false;
José Fonseca2e8febd2011-04-09 00:10:30 +0100268 space = 0;
José Fonseca589082d2011-03-30 09:10:40 +0100269 }
270
271 inline void endArray(void) {
José Fonsecafc92b762011-04-08 09:44:26 +0100272 --level;
José Fonseca2e8febd2011-04-09 00:10:30 +0100273 if (space == '\n') {
274 newline();
275 }
José Fonseca589082d2011-03-30 09:10:40 +0100276 os << "]";
277 value = true;
José Fonsecafc92b762011-04-08 09:44:26 +0100278 space = '\n';
José Fonseca589082d2011-03-30 09:10:40 +0100279 }
280
281 inline void writeString(const char *s) {
José Fonseca02bf5b42011-10-11 19:33:02 +0100282 if (!s) {
283 writeNull();
284 return;
285 }
286
José Fonseca589082d2011-03-30 09:10:40 +0100287 separator();
José Fonseca702c1c62011-04-08 07:58:05 +0100288 escapeUnicodeString(s);
José Fonsecaed2167c2011-03-31 01:15:23 +0100289 value = true;
José Fonsecafc92b762011-04-08 09:44:26 +0100290 space = ' ';
José Fonseca589082d2011-03-30 09:10:40 +0100291 }
292
José Fonsecaa15b7fe2011-07-16 21:08:16 -0700293 inline void writeString(const std::string &s) {
294 writeString(s.c_str());
295 }
296
José Fonseca23a17d62011-04-09 12:22:58 +0100297 inline void writeBase64(const void *bytes, size_t size) {
298 separator();
299 encodeBase64String((const unsigned char *)bytes, size);
300 value = true;
301 space = ' ';
302 }
303
José Fonseca589082d2011-03-30 09:10:40 +0100304 inline void writeNull(void) {
305 separator();
306 os << "null";
307 value = true;
José Fonsecafc92b762011-04-08 09:44:26 +0100308 space = ' ';
José Fonseca589082d2011-03-30 09:10:40 +0100309 }
310
311 inline void writeBool(bool b) {
312 separator();
313 os << (b ? "true" : "false");
314 value = true;
José Fonsecafc92b762011-04-08 09:44:26 +0100315 space = ' ';
José Fonseca589082d2011-03-30 09:10:40 +0100316 }
317
José Fonseca7ec90502012-04-16 20:09:42 +0100318
319 /**
320 * Special case for char to prevent it to be written as a literal
321 * character.
322 */
José Fonseca8739fa62012-11-15 13:36:09 +0000323 inline void writeInt(signed char n) {
José Fonseca7ec90502012-04-16 20:09:42 +0100324 separator();
325 os << std::dec << static_cast<int>(n);
326 value = true;
327 space = ' ';
328 }
329
José Fonseca8739fa62012-11-15 13:36:09 +0000330 inline void writeInt(unsigned char n) {
José Fonseca7ec90502012-04-16 20:09:42 +0100331 separator();
332 os << std::dec << static_cast<unsigned>(n);
333 value = true;
334 space = ' ';
335 }
336
José Fonseca589082d2011-03-30 09:10:40 +0100337 template<class T>
José Fonseca8739fa62012-11-15 13:36:09 +0000338 inline void writeInt(T n) {
339 separator();
340 os << std::dec << n;
341 value = true;
342 space = ' ';
343 }
344 template<class T>
345 inline void writeFloat(T n) {
José Fonsecac3912652012-10-26 23:45:35 +0100346 separator();
347 if (isnan(n)) {
348 // NaN is non-standard but widely supported
349 os << "NaN";
350 } else if (!isfinite(n)) {
351 // Infinite is non-standard but widely supported
352 if (n < 0) {
353 os << '-';
354 }
355 os << "Infinity";
José Fonseca364a5a62011-05-06 20:49:45 +0100356 } else {
José Fonseca5f2245e2012-05-14 20:20:44 +0100357 os << std::dec << std::setprecision(std::numeric_limits<T>::digits10 + 1) << n;
José Fonseca364a5a62011-05-06 20:49:45 +0100358 }
José Fonsecac3912652012-10-26 23:45:35 +0100359 value = true;
360 space = ' ';
José Fonseca589082d2011-03-30 09:10:40 +0100361 }
José Fonseca23a17d62011-04-09 12:22:58 +0100362
363 inline void writeStringMember(const char *name, const char *s) {
364 beginMember(name);
365 writeString(s);
366 endMember();
367 }
368
369 inline void writeBoolMember(const char *name, bool b) {
370 beginMember(name);
371 writeBool(b);
372 endMember();
373 }
374
375 template<class T>
José Fonseca8739fa62012-11-15 13:36:09 +0000376 inline void writeIntMember(const char *name, T n) {
José Fonseca23a17d62011-04-09 12:22:58 +0100377 beginMember(name);
José Fonseca8739fa62012-11-15 13:36:09 +0000378 writeInt(n);
José Fonseca23a17d62011-04-09 12:22:58 +0100379 endMember();
380 }
José Fonseca589082d2011-03-30 09:10:40 +0100381};
382
383#endif /* _JSON_HPP_ */