]> git.cworth.org Git - apitrace/blob - json.hpp
Fix escaping of json unicode characters.
[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
49     void newline(void) {
50         os << "\n";
51         for (int i = 0; i < level; ++i) 
52             os << "  ";
53     }
54
55     void separator(void) {
56         if (value) {
57             os << ",";
58         }
59     }
60
61     void escapeAsciiString(const char *str) {
62         os << "\"";
63
64         const unsigned char *src = (const unsigned char *)str;
65         unsigned char c;
66         while ((c = *src++)) {
67             if ((c == '\"') ||
68                 (c == '\\')) {
69                 // escape character
70                 os << '\\' << (unsigned char)c;
71             } else if ((c >= 0x20 && c <= 0x7e) ||
72                         c == '\t' ||
73                         c == '\r' ||
74                         c == '\n') {
75                 // pass-through character
76                 os << (unsigned char)c;
77             } else {
78                 assert(0);
79                 os << "?";
80             }
81         }
82
83         os << "\"";
84     }
85
86     void escapeUnicodeString(const char *str) {
87         os << "\"";
88
89         const char *locale = setlocale(LC_CTYPE, "");
90         const char *src = str;
91         mbstate_t state;
92
93         memset(&state, 0, sizeof state);
94
95         do {
96             // Convert characters one at a time in order to recover from
97             // conversion errors
98             wchar_t c;
99             size_t written = mbsrtowcs(&c, &src, 1, &state);
100             if (written == 0) {
101                 // completed
102                 break;
103             } if (written == (size_t)-1) {
104                 // conversion error -- skip 
105                 os << "?";
106                 do {
107                     ++src;
108                 } while (*src & 0x80);
109             } else if ((c == '\"') ||
110                        (c == '\\')) {
111                 // escape character
112                 os << '\\' << (unsigned char)c;
113             } else if ((c >= 0x20 && c <= 0x7e) ||
114                         c == '\t' ||
115                         c == '\r' ||
116                         c == '\n') {
117                 // pass-through character
118                 os << (unsigned char)c;
119             } else {
120                 // unicode
121                 os << "\\u" << std::setfill('0') << std::hex << std::setw(4) << (unsigned)c;
122                 os << std::dec;
123             }
124         } while (src);
125
126         setlocale(LC_CTYPE, locale);
127
128         os << "\"";
129     }
130
131 public:
132     JSONWriter(std::ostream &_os) : 
133         os(_os), 
134         level(0),
135         value(false)
136     {
137         beginObject();
138     }
139
140     ~JSONWriter() {
141         endObject();
142         newline();
143     }
144
145     inline void beginObject() {
146         separator();
147         os << "{";
148         ++level;
149         value = false;
150     }
151
152     inline void endObject() {
153         --level;
154         if (value)
155             newline();
156         os << "}";
157         value = true;
158     }
159
160     inline void beginMember(const char * name) {
161         separator();
162         newline();
163         escapeAsciiString(name);
164         os << ": ";
165         value = false;
166     }
167
168     inline void endMember(void) {
169         assert(value);
170         value = true;
171     }
172
173     inline void beginArray() {
174         separator();
175         os << "[";
176         value = false;
177     }
178
179     inline void endArray(void) {
180         os << "]";
181         value = true;
182     }
183
184     inline void writeString(const char *s) {
185         separator();
186         escapeUnicodeString(s);
187         value = true;
188     }
189
190     inline void writeNull(void) {
191         separator();
192         os << "null";
193         value = true;
194     }
195
196     inline void writeBool(bool b) {
197         separator();
198         os << (b ? "true" : "false");
199         value = true;
200     }
201
202     template<class T>
203     void writeNumber(T n) {
204         separator();
205         os << std::dec << n;
206         value = true;
207     }
208 };
209
210 #endif /* _JSON_HPP_ */