]> git.cworth.org Git - apitrace/blob - retrace/json.hpp
Merge remote-tracking branch 'github/master' into d2d
[apitrace] / retrace / json.hpp
1 /**************************************************************************
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>
35 #include <wchar.h>
36
37 #ifdef _MSC_VER
38 #  include <float.h>
39 #  define isfinite _finite
40 #else
41 #  include <math.h> // isfinite
42 #endif
43
44 #include <iomanip>
45 #include <limits>
46 #include <ostream>
47 #include <string>
48
49
50 class JSONWriter
51 {
52 private:
53     std::ostream &os;
54
55     int level;
56     bool value;
57     char space;
58
59     void newline(void) {
60         os << "\n";
61         for (int i = 0; i < level; ++i) 
62             os << "  ";
63     }
64
65     void separator(void) {
66         if (value) {
67             os << ",";
68             switch (space) {
69             case '\0':
70                 break;
71             case '\n':
72                 newline();
73                 break;
74             default:
75                 os << space;
76                 break;
77             }
78         } else {
79             if (space == '\n') {
80                 newline();
81             }
82         }
83     }
84
85     void escapeAsciiString(const char *str) {
86         os << "\"";
87
88         const unsigned char *src = (const unsigned char *)str;
89         unsigned char c;
90         while ((c = *src++)) {
91             if ((c == '\"') ||
92                 (c == '\\')) {
93                 // escape character
94                 os << '\\' << (unsigned char)c;
95             } else if ((c >= 0x20 && c <= 0x7e) ||
96                         c == '\t' ||
97                         c == '\r' ||
98                         c == '\n') {
99                 // pass-through character
100                 os << (unsigned char)c;
101             } else {
102                 assert(0);
103                 os << "?";
104             }
105         }
106
107         os << "\"";
108     }
109
110     void escapeUnicodeString(const char *str) {
111         os << "\"";
112
113         const char *locale = setlocale(LC_CTYPE, "");
114         const char *src = str;
115         mbstate_t state;
116
117         memset(&state, 0, sizeof state);
118
119         do {
120             // Convert characters one at a time in order to recover from
121             // conversion errors
122             wchar_t c;
123             size_t written = mbsrtowcs(&c, &src, 1, &state);
124             if (written == 0) {
125                 // completed
126                 break;
127             } if (written == (size_t)-1) {
128                 // conversion error -- skip 
129                 os << "?";
130                 do {
131                     ++src;
132                 } while (*src & 0x80);
133             } else if ((c == '\"') ||
134                        (c == '\\')) {
135                 // escape character
136                 os << '\\' << (unsigned char)c;
137             } else if ((c >= 0x20 && c <= 0x7e) ||
138                         c == '\t' ||
139                         c == '\r' ||
140                         c == '\n') {
141                 // pass-through character
142                 os << (unsigned char)c;
143             } else {
144                 // unicode
145                 os << "\\u" << std::setfill('0') << std::hex << std::setw(4) << (unsigned)c;
146                 os << std::dec;
147             }
148         } while (src);
149
150         setlocale(LC_CTYPE, locale);
151
152         os << "\"";
153     }
154
155     void encodeBase64String(const unsigned char *bytes, size_t size) {
156         const char *table64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
157         unsigned char c0, c1, c2, c3;
158         char buf[4];
159         unsigned written;
160
161         os << "\"";
162
163         written = 0;
164         while (size >= 3) {
165             c0 = bytes[0] >> 2;
166             c1 = ((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xf0) >> 4);
167             c2 = ((bytes[1] & 0x0f) << 2) | ((bytes[2] & 0xc0) >> 6);
168             c3 = bytes[2] & 0x3f;
169
170             buf[0] = table64[c0];
171             buf[1] = table64[c1];
172             buf[2] = table64[c2];
173             buf[3] = table64[c3];
174
175             os.write(buf, 4);
176
177             bytes += 3;
178             size -= 3;
179             ++written;
180
181             if (written >= 76/4 && size) {
182                 os << "\n";
183                 written = 0;
184             }
185         }
186
187         if (size > 0) {
188             c0 = bytes[0] >> 2;
189             c1 = ((bytes[0] & 0x03) << 4);
190             buf[2] = '=';
191             buf[3] = '=';
192             
193             if (size > 1) {
194                 c1 |= ((bytes[1] & 0xf0) >> 4);
195                 c2 = ((bytes[1] & 0x0f) << 2);
196                 if (size > 2) {
197                     c2 |= ((bytes[2] & 0xc0) >> 6);
198                     c3 = bytes[2] & 0x3f;
199                     buf[3] = table64[c3];
200                 }
201                 buf[2] = table64[c2];
202             }
203             buf[1] = table64[c1];
204             buf[0] = table64[c0];
205
206             os.write(buf, 4);
207         }
208
209         os << "\"";
210     }
211
212 public:
213     JSONWriter(std::ostream &_os) : 
214         os(_os), 
215         level(0),
216         value(false),
217         space(0)
218     {
219         beginObject();
220     }
221
222     ~JSONWriter() {
223         endObject();
224         newline();
225     }
226
227     inline void beginObject() {
228         separator();
229         os << "{";
230         ++level;
231         value = false;
232     }
233
234     inline void endObject() {
235         --level;
236         if (value)
237             newline();
238         os << "}";
239         value = true;
240         space = '\n';
241     }
242
243     inline void beginMember(const char * name) {
244         space = 0;
245         separator();
246         newline();
247         escapeAsciiString(name);
248         os << ": ";
249         value = false;
250     }
251
252     inline void beginMember(const std::string &name) {
253         beginMember(name.c_str());
254     }
255
256     inline void endMember(void) {
257         assert(value);
258         value = true;
259         space = 0;
260     }
261
262     inline void beginArray() {
263         separator();
264         os << "[";
265         ++level;
266         value = false;
267         space = 0;
268     }
269
270     inline void endArray(void) {
271         --level;
272         if (space == '\n') {
273             newline();
274         }
275         os << "]";
276         value = true;
277         space = '\n';
278     }
279
280     inline void writeString(const char *s) {
281         if (!s) {
282             writeNull();
283             return;
284         }
285
286         separator();
287         escapeUnicodeString(s);
288         value = true;
289         space = ' ';
290     }
291
292     inline void writeString(const std::string &s) {
293         writeString(s.c_str());
294     }
295
296     inline void writeBase64(const void *bytes, size_t size) {
297         separator();
298         encodeBase64String((const unsigned char *)bytes, size);
299         value = true;
300         space = ' ';
301     }
302
303     inline void writeNull(void) {
304         separator();
305         os << "null";
306         value = true;
307         space = ' ';
308     }
309
310     inline void writeBool(bool b) {
311         separator();
312         os << (b ? "true" : "false");
313         value = true;
314         space = ' ';
315     }
316
317
318     /**
319      * Special case for char to prevent it to be written as a literal
320      * character.
321      */
322     inline void writeNumber(char n) {
323         separator();
324         os << std::dec << static_cast<int>(n);
325         value = true;
326         space = ' ';
327     }
328
329     inline void writeNumber(unsigned char n) {
330         separator();
331         os << std::dec << static_cast<unsigned>(n);
332         value = true;
333         space = ' ';
334     }
335
336     template<class T>
337     inline void writeNumber(T n) {
338         if (!isfinite(n)) {
339             // NaN/Inf
340             writeNull();
341         } else {
342             separator();
343             os << std::dec << std::setprecision(std::numeric_limits<T>::digits10 + 1) << n;
344             value = true;
345             space = ' ';
346         }
347     }
348     
349     inline void writeStringMember(const char *name, const char *s) {
350         beginMember(name);
351         writeString(s);
352         endMember();
353     }
354
355     inline void writeBoolMember(const char *name, bool b) {
356         beginMember(name);
357         writeBool(b);
358         endMember();
359     }
360
361     template<class T>
362     inline void writeNumberMember(const char *name, T n) {
363         beginMember(name);
364         writeNumber(n);
365         endMember();
366     }
367 };
368
369 #endif /* _JSON_HPP_ */