#include <assert.h>
#include <stddef.h>
+#include <wchar.h>
#include <ostream>
+#include <iomanip>
class JSONWriter
int level;
bool value;
+ char space;
void newline(void) {
os << "\n";
void separator(void) {
if (value) {
os << ",";
+ switch (space) {
+ case '\0':
+ break;
+ case '\n':
+ newline();
+ break;
+ default:
+ os << space;
+ break;
+ }
+ } else {
+ if (space == '\n') {
+ newline();
+ }
}
}
- void escapeString(const char *str) {
- const unsigned char *p = (const unsigned char *)str;
+ void escapeAsciiString(const char *str) {
os << "\"";
+
+ const unsigned char *src = (const unsigned char *)str;
unsigned char c;
- while ((c = *p++) != 0) {
- if (c == '\"')
- os << "\\\"";
- else if (c == '\\')
- os << "\\\\";
- else if (c >= 0x20 && c <= 0x7e)
- os << c;
- else if (c == '\t')
- os << "\\t";
- else if (c == '\r')
- os << "\\r";
- else if (c == '\n')
- os << " ";
- else {
- unsigned octal0 = c & 0x7;
- unsigned octal1 = (c >> 3) & 0x7;
- unsigned octal2 = (c >> 3) & 0x7;
- os << "\\";
- if (octal2)
- os << octal2;
- if (octal1)
- os << octal1;
- os << octal0;
+ while ((c = *src++)) {
+ if ((c == '\"') ||
+ (c == '\\')) {
+ // escape character
+ os << '\\' << (unsigned char)c;
+ } else if ((c >= 0x20 && c <= 0x7e) ||
+ c == '\t' ||
+ c == '\r' ||
+ c == '\n') {
+ // pass-through character
+ os << (unsigned char)c;
+ } else {
+ assert(0);
+ os << "?";
+ }
+ }
+
+ os << "\"";
+ }
+
+ void escapeUnicodeString(const char *str) {
+ os << "\"";
+
+ const char *locale = setlocale(LC_CTYPE, "");
+ const char *src = str;
+ mbstate_t state;
+
+ memset(&state, 0, sizeof state);
+
+ do {
+ // Convert characters one at a time in order to recover from
+ // conversion errors
+ wchar_t c;
+ size_t written = mbsrtowcs(&c, &src, 1, &state);
+ if (written == 0) {
+ // completed
+ break;
+ } if (written == (size_t)-1) {
+ // conversion error -- skip
+ os << "?";
+ do {
+ ++src;
+ } while (*src & 0x80);
+ } else if ((c == '\"') ||
+ (c == '\\')) {
+ // escape character
+ os << '\\' << (unsigned char)c;
+ } else if ((c >= 0x20 && c <= 0x7e) ||
+ c == '\t' ||
+ c == '\r' ||
+ c == '\n') {
+ // pass-through character
+ os << (unsigned char)c;
+ } else {
+ // unicode
+ os << "\\u" << std::setfill('0') << std::hex << std::setw(4) << (unsigned)c;
+ os << std::dec;
+ }
+ } while (src);
+
+ setlocale(LC_CTYPE, locale);
+
+ os << "\"";
+ }
+
+ void encodeBase64String(const unsigned char *bytes, size_t size) {
+ const char *table64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned char c0, c1, c2, c3;
+ char buf[4];
+ unsigned written;
+
+ os << "\"";
+
+ written = 0;
+ while (size >= 3) {
+ c0 = bytes[0] >> 2;
+ c1 = ((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xf0) >> 4);
+ c2 = ((bytes[1] & 0x0f) << 2) | ((bytes[2] & 0xc0) >> 6);
+ c3 = bytes[2] & 0x3f;
+
+ buf[0] = table64[c0];
+ buf[1] = table64[c1];
+ buf[2] = table64[c2];
+ buf[3] = table64[c3];
+
+ os.write(buf, 4);
+
+ bytes += 3;
+ size -= 3;
+ ++written;
+
+ if (written >= 76/4 && size) {
+ os << "\n";
+ written = 0;
+ }
+ }
+
+ if (size > 0) {
+ c0 = bytes[0] >> 2;
+ c1 = ((bytes[0] & 0x03) << 4);
+ buf[2] = '=';
+ buf[3] = '=';
+
+ if (size > 1) {
+ c1 |= ((bytes[1] & 0xf0) >> 4);
+ c2 = ((bytes[1] & 0x0f) << 2);
+ if (size > 2) {
+ c2 |= ((bytes[2] & 0xc0) >> 6);
+ c3 = bytes[2] & 0x3f;
+ buf[3] = table64[c3];
+ }
+ buf[2] = table64[c2];
}
+ buf[1] = table64[c1];
+ buf[0] = table64[c0];
+
+ os.write(buf, 4);
}
+
os << "\"";
}
JSONWriter(std::ostream &_os) :
os(_os),
level(0),
- value(false)
+ value(false),
+ space(0)
{
beginObject();
}
}
inline void beginObject() {
+ separator();
os << "{";
++level;
value = false;
newline();
os << "}";
value = true;
+ space = '\n';
}
inline void beginMember(const char * name) {
+ space = 0;
separator();
newline();
- escapeString(name);
+ escapeAsciiString(name);
os << ": ";
value = false;
}
inline void endMember(void) {
assert(value);
value = true;
+ space = 0;
}
inline void beginArray() {
separator();
os << "[";
+ ++level;
value = false;
+ space = 0;
}
inline void endArray(void) {
+ --level;
+ if (space == '\n') {
+ newline();
+ }
os << "]";
value = true;
+ space = '\n';
}
inline void writeString(const char *s) {
separator();
- escapeString(s);
+ escapeUnicodeString(s);
+ value = true;
+ space = ' ';
+ }
+
+ inline void writeBase64(const void *bytes, size_t size) {
+ separator();
+ encodeBase64String((const unsigned char *)bytes, size);
value = true;
+ space = ' ';
}
inline void writeNull(void) {
separator();
os << "null";
value = true;
+ space = ' ';
}
inline void writeBool(bool b) {
separator();
os << (b ? "true" : "false");
value = true;
+ space = ' ';
}
template<class T>
- void writeNumber(T n) {
+ inline void writeNumber(T n) {
separator();
- os << n;
+ os << std::dec << n;
value = true;
+ space = ' ';
+ }
+
+ inline void writeStringMember(const char *name, const char *s) {
+ beginMember(name);
+ writeString(s);
+ endMember();
+ }
+
+ inline void writeBoolMember(const char *name, bool b) {
+ beginMember(name);
+ writeBool(b);
+ endMember();
+ }
+
+ template<class T>
+ inline void writeNumberMember(const char *name, T n) {
+ beginMember(name);
+ writeNumber(n);
+ endMember();
}
};