]> git.cworth.org Git - apitrace/blob - retrace/json.hpp
d3dretrace: Eliminate differences between d3d9/d3d10.
[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 #  define isnan _isnan
41 #else
42 #  include <math.h> // isfinite, isnan
43 #endif
44
45 #include <iomanip>
46 #include <limits>
47 #include <ostream>
48 #include <string>
49
50
51 class JSONWriter
52 {
53 private:
54     std::ostream &os;
55
56     int level;
57     bool value;
58     char space;
59
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 << ",";
69             switch (space) {
70             case '\0':
71                 break;
72             case '\n':
73                 newline();
74                 break;
75             default:
76                 os << space;
77                 break;
78             }
79         } else {
80             if (space == '\n') {
81                 newline();
82             }
83         }
84     }
85
86     void escapeAsciiString(const char *str) {
87         os << "\"";
88
89         const unsigned char *src = (const unsigned char *)str;
90         unsigned char c;
91         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 << "?";
105             }
106         }
107
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
153         os << "\"";
154     }
155
156     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
182             if (written >= 76/4 && size) {
183                 os << "\n";
184                 written = 0;
185             }
186         }
187
188         if (size > 0) {
189             c0 = bytes[0] >> 2;
190             c1 = ((bytes[0] & 0x03) << 4);
191             buf[2] = '=';
192             buf[3] = '=';
193             
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];
201                 }
202                 buf[2] = table64[c2];
203             }
204             buf[1] = table64[c1];
205             buf[0] = table64[c0];
206
207             os.write(buf, 4);
208         }
209
210         os << "\"";
211     }
212
213 public:
214     JSONWriter(std::ostream &_os) : 
215         os(_os), 
216         level(0),
217         value(false),
218         space(0)
219     {
220         beginObject();
221     }
222
223     ~JSONWriter() {
224         endObject();
225         newline();
226     }
227
228     inline void beginObject() {
229         separator();
230         os << "{";
231         ++level;
232         value = false;
233     }
234
235     inline void endObject() {
236         --level;
237         if (value)
238             newline();
239         os << "}";
240         value = true;
241         space = '\n';
242     }
243
244     inline void beginMember(const char * name) {
245         space = 0;
246         separator();
247         newline();
248         escapeAsciiString(name);
249         os << ": ";
250         value = false;
251     }
252
253     inline void beginMember(const std::string &name) {
254         beginMember(name.c_str());
255     }
256
257     inline void endMember(void) {
258         assert(value);
259         value = true;
260         space = 0;
261     }
262
263     inline void beginArray() {
264         separator();
265         os << "[";
266         ++level;
267         value = false;
268         space = 0;
269     }
270
271     inline void endArray(void) {
272         --level;
273         if (space == '\n') {
274             newline();
275         }
276         os << "]";
277         value = true;
278         space = '\n';
279     }
280
281     inline void writeString(const char *s) {
282         if (!s) {
283             writeNull();
284             return;
285         }
286
287         separator();
288         escapeUnicodeString(s);
289         value = true;
290         space = ' ';
291     }
292
293     inline void writeString(const std::string &s) {
294         writeString(s.c_str());
295     }
296
297     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
304     inline void writeNull(void) {
305         separator();
306         os << "null";
307         value = true;
308         space = ' ';
309     }
310
311     inline void writeBool(bool b) {
312         separator();
313         os << (b ? "true" : "false");
314         value = true;
315         space = ' ';
316     }
317
318
319     /**
320      * Special case for char to prevent it to be written as a literal
321      * character.
322      */
323     inline void writeNumber(char n) {
324         separator();
325         os << std::dec << static_cast<int>(n);
326         value = true;
327         space = ' ';
328     }
329
330     inline void writeNumber(unsigned char n) {
331         separator();
332         os << std::dec << static_cast<unsigned>(n);
333         value = true;
334         space = ' ';
335     }
336
337     template<class T>
338     inline void writeNumber(T n) {
339         separator();
340         if (isnan(n)) {
341             // NaN is non-standard but widely supported
342             os << "NaN";
343         } else if (!isfinite(n)) {
344             // Infinite is non-standard but widely supported
345             if (n < 0) {
346                 os << '-';
347             }
348             os << "Infinity";
349         } else {
350             os << std::dec << std::setprecision(std::numeric_limits<T>::digits10 + 1) << n;
351         }
352         value = true;
353         space = ' ';
354     }
355     
356     inline void writeStringMember(const char *name, const char *s) {
357         beginMember(name);
358         writeString(s);
359         endMember();
360     }
361
362     inline void writeBoolMember(const char *name, bool b) {
363         beginMember(name);
364         writeBool(b);
365         endMember();
366     }
367
368     template<class T>
369     inline void writeNumberMember(const char *name, T n) {
370         beginMember(name);
371         writeNumber(n);
372         endMember();
373     }
374 };
375
376 #endif /* _JSON_HPP_ */