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