X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=json.hpp;h=9e6b96004dc71be9dc96e85a6d4279f508c12765;hb=3a8ffad2f31f4438e19dd5a0f1c068e1fdffdeea;hp=feaf1eafdc7753153d6505a4218da2323845c8ca;hpb=3b4677e4c06ad64056587911e690dfd549d83358;p=apitrace diff --git a/json.hpp b/json.hpp index feaf1ea..9e6b960 100644 --- a/json.hpp +++ b/json.hpp @@ -32,8 +32,11 @@ #include #include +#include +#include #include +#include class JSONWriter @@ -43,6 +46,7 @@ private: int level; bool value; + char space; void newline(void) { os << "\n"; @@ -53,38 +57,147 @@ private: 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') + 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"; - 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; + 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 << "\""; } @@ -92,7 +205,8 @@ public: JSONWriter(std::ostream &_os) : os(_os), level(0), - value(false) + value(false), + space(0) { beginObject(); } @@ -115,55 +229,108 @@ public: 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 beginMember(const std::string &name) { + beginMember(name.c_str()); + } + 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 writeString(const std::string &s) { + writeString(s.c_str()); + } + + 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 - void writeNumber(T n) { - separator(); - os << n; - value = true; + inline void writeNumber(T n) { + if (n != n) { + // NaN + writeNull(); + } else { + separator(); + os << std::dec << std::setprecision(9) << 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 + inline void writeNumberMember(const char *name, T n) { + beginMember(name); + writeNumber(n); + endMember(); } };