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