#include "json.h" namespace ssjson { namespace _impl { typedef std::string Buffer; template inline void DumpNumber(Buf &res, const char *fmt, T&&...val) { char buf[64] = {0}; int n = snprintf(buf, sizeof(buf)-1, fmt, val...); res.append(buf, n); } inline void DumpFloat(Buffer &res, const long double fval) { DumpNumber(res, "%.*Lg", std::numeric_limits::digits10, fval); } inline void DumpInt(Buffer &res, const long long ival) { DumpNumber(res, "%lld", ival); } // inline void DumpFloat(Buffer &res, const double fval) { DumpNumber(res, "%.*g", std::numeric_limits::digits10, fval); } // inline void DumpInt(Buffer &res, const long ival) { DumpNumber(res, "%ld", ival); } // assume utf8 string. void DumpString(Buffer &res, const std::string &s, const bool escape_unicode) { res += '\"'; auto EscapeSpecialChar = [&](const int c) { // ( \b, \f, \t, \n, \r, \", \\, / ). const char esc_table[] = { 0 , 0, 0 , 0 , 0 , 0, 0, 0 , 'b', 't', 'n', 0, 'f','r', 0 , 0, 0, 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0, 0, 0 , 0 , 0 , 0 , 0, 0 , 0 ,'\"', 0, 0, 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0, 0, '/', 0 , 0 , 0 , 0, 0 , 0 , 0 , 0, 0, 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0, 0, 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0, 0, 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0, 0, 0 , 0 , 0 , 0 , 0,'\\', }; res += '\\'; res += esc_table[c]; }; auto AddUnicode = [&](unsigned u) { auto HexChar = [](unsigned char uc) { return "0123456789abcdef"[uc & 0xF]; }; char buf[6] = { '\\', 'u', HexChar(u>>12), HexChar(u>>8), HexChar(u>>4), HexChar(u) }; res.append(buf, buf+6); }; auto IsControl = [](const unsigned char c) { return c < 0x20 || c == 0x7F; }; if (escape_unicode) { for (unsigned i = 0; i < s.size(); ++i) { switch(s[i]) { case '\b': case '\f': case '\t': case '\n': case '\r': case '\"': case '\\': case '/' : EscapeSpecialChar(s[i]); break; default : { const unsigned char &uc = (unsigned char &)s[i]; const unsigned char *sz = &uc; if (IsControl(uc)) { // control characters AddUnicode(uc); } else if (uc < 0x80) { res += s[i]; } else if (uc < 0xE0) { // 2 bytes AddUnicode(((sz[0]&0x1F)<<6) | (sz[1] & 0x3F)); i += 1; } else if (uc < 0xF0) { // 3 bytes AddUnicode(((sz[0]&0x0F)<<12) | ((sz[1]&0x3F)<<6) | (sz[2] & 0x3F)); i += 2; } else { res += s[i]; // exceed 0xFFFF. } } break; } } } else { for (unsigned i = 0; i < s.size(); ++i) { switch(s[i]) { case '\b': case '\f': case '\t': case '\n': case '\r': case '\"': case '\\': case '/' : EscapeSpecialChar(s[i]); break; default : if (IsControl(s[i])) { // control characters AddUnicode(s[i]); } else { res += s[i]; } break; } } } res += '\"'; }; void Dump(const JValue &jv, Buffer &res, JValue::DumpOptions &opt, const bool pretty) { auto NewLine = [&]() { res += '\n'; res.append(opt.indent_level_ * opt.indent_step_, ' '); }; switch (jv.type()) { case JValue::jv_object: { res += '{'; auto &obj = jv.object(); if (!obj.empty()) { if (pretty) { opt.indent_level_++; for (auto &&kv : obj) { NewLine(); DumpString(res, kv.first, opt.escape_unicode_); res.append(": ", 2); Dump(kv.second, res, opt, pretty); res += ','; } res.pop_back(); // remove last ','; opt.indent_level_--; NewLine(); } else { for (auto &&kv : obj) { DumpString(res, kv.first, opt.escape_unicode_); res += ':'; Dump(kv.second, res, opt, pretty); res += ','; } res.pop_back(); // remove last ','; } } res += '}'; } break; case JValue::jv_array: { res += '['; auto &arr = jv.array(); if (!arr.empty()) { if (pretty) { opt.indent_level_++; for (auto &&v : arr) { NewLine(); Dump(v, res, opt, pretty); res += ','; } res.pop_back(); // remove last ','; opt.indent_level_--; NewLine(); } else { for (auto &&v : arr) { Dump(v, res, opt, pretty); res += ','; } res.pop_back(); // remove last ','; } } res += ']'; } break; case JValue::jv_float: { auto idx = res.size(); DumpFloat(res, jv.get_value()); while (idx < res.size() && res[idx] != '.') { ++idx; } if (idx == res.size()) { res.append(".0", 2); } } break; case JValue::jv_null: res.append("null", 4); break; case JValue::jv_int: DumpInt(res, jv.get_value()); break; case JValue::jv_bool: if (jv.get_value()) { res.append("true", 4); } else { res.append("false", 5); } break; case JValue::jv_string: DumpString(res, jv.get_value(), opt.escape_unicode_); break; default: std::invalid_argument("invalid json type:" + std::to_string(jv.type())); break; } } auto HexCharVal = [](const unsigned char c) { static const unsigned char hex_char_table[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; return hex_char_table[c]; }; } // namespace _impl int JValue::parse(const char *begin, const char *end) { using namespace _impl; const char *p = begin; auto Error = [&](const std::string &msg = "invalid value at"){ throw std::invalid_argument(msg + " : "+ std::string(p, std::min(int(end-p), 30))); }; auto IsSpace = [](const char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; }; auto IgnoreSpaces = [&]() { while (p != end && IsSpace(*p)) { ++p; } }; auto Peek = [&]() { IgnoreSpaces(); if (p == end) { Error("input ends unexpectedly!"); } return *p; }; auto Expect = [&](const char c) { if (Peek() != c) { Error(std::string("parse error, expecting ") + c); } }; auto parse_string = [&]() { std::string res; auto to_utf8 = [&]() { auto Hex4 = [](const char *sz) { auto FromHex = [](const unsigned char c)->unsigned char { const unsigned char v = HexCharVal(c); if (v == 0xFF) { throw std::invalid_argument(std::string("invalid unicode input ") + char(c)); } return v; }; return (FromHex(sz[0]) << 12) | (FromHex(sz[1]) << 8) | (FromHex(sz[2]) << 4) | (FromHex(sz[3])); }; const unsigned int u = Hex4(p+2); p += 4; if (u < 0x80) { res += char(u); } else if (u < 0x800) { res += char((u >> 6) | 0xC0); res += char((u & 0x3F) | 0x80); } else if (u < 0x10000) { res += char((u >> 12) | 0xE0); res += char(((u >> 6)& 0x3F) | 0x80); res += char((u & 0x3F) | 0x80); } else if (u < 0x110000) { res += char((u >> 18) | 0xF0); res += char(((u >> 12)& 0x3F) | 0x80); res += char(((u >> 6)& 0x3F) | 0x80); res += char((u & 0x3F) | 0x80); } else { Error("invalid unicode codepoint"); } }; auto ParseEscapedChar = [&](const int c) { const char unesc_table[] = { 0 , '\b', 0 , 0 , 0 , '\f', 0 , 0 , 0 , 0 , 0 , 0 , 0 , '\n', 0 , 0 , 0 , '\r', 0 , '\t', }; res += unesc_table[c-'a']; }; unsigned left = 0; auto AddLeft = [&] { if (left != 0) { res.append(p-left, left); left = 0; } }; while (++p != end) { switch(*p) { case '\"': AddLeft(); ++p; return res; case '\\': AddLeft(); if (p+1 != end) { switch (p[1]) { case 'b' : case 'f' : case 'n' : case 'r' : case 't' : ParseEscapedChar(p[1]); break; case 'u' : to_utf8(); break; case '\"': case '\\': case '/' : ++left; break; default: left += 2; break; } ++p; // parsed 2 char. } // else, next round will fail. break; // and escaped char should not appear. case '\t': case '\n': case '\r': case '\b': case '\f': //case '/' : AddLeft(); Error("invalid string content") ; break; default: ++left; break; } } Error("string ends unexpectedly!"); return res; }; auto parse_object = [&]() { assert(*p == '{'); ++p; if (Peek() == '}') { ++p; return object_type(); } object_type obj; while (true) { Expect('\"'); std::string key(parse_string()); Expect(':'); ++p; JValue v; p += v.parse(p, end); obj.emplace(std::move(key), std::move(v)); switch (Peek()) { case ',': ++p; break; case '}': ++p; return obj; default: Error("parse object error"); } } }; auto parse_array = [&]() { assert(*p == '['); ++p; if (Peek() == ']') { ++p; return array_type(); } array_type arr; while (true) { JValue v; p += v.parse(p, end); arr.emplace_back(std::move(v)); switch (Peek()) { case ',': ++p; break; case ']': ++p; return arr; default: Error("parse array error"); } } }; auto ExpectText = [&](const char *sz, const size_t len) { if(memcmp(p+1, sz+1, len-1) == 0) { p += len; } else { Error("parse literal error, expecting '" + std::string(sz, len) + "', read " + std::string(p, len).c_str()); } }; switch (Peek()) { case '{': *this = parse_object(); break; case '[': *this = parse_array(); break; case '\"': *this = parse_string(); break; case 't': ExpectText("true", 4); *this = true; break; case 'f': ExpectText("false", 5); *this = false; break; case 'n': ExpectText("null", 4); *this = nullptr; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': { auto IsNumber = [](const char c) { return '0' <= c && c <= '9'; }; auto sep = p+1; while (sep != end && IsNumber(*sep)) { ++sep; } char *parse_end = (char*)p; switch (*sep) { case '.' : case 'e' : case 'E': *this = strtold((char*)p, &parse_end); break; default: *this = strtoll((char*)p, &parse_end, 10); break; } p = parse_end; } break; default : Error(); break; } return p - begin; } std::string JValue::dump(const DumpOptions &opt_in) const { using namespace _impl; Buffer res; DumpOptions opt(opt_in); if(opt.indent_step_ > 0) { res.append(opt.indent_level_ * opt.indent_step_, ' '); } Dump(*this, res, opt, opt.indent_step_ > 0); return res; } } // jsonpp