From 92901dfcfb037a469784242f28688decd3ab3d65 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 25 Jan 2017 10:26:08 +0100 Subject: [PATCH] Backport json11 fixes from upstream (cherry picked from commit 3c20dd3b30bd0c15c5f7a1e82fba3bb5254b28df) --- ext/json11/json11.cpp | 150 +++++++++++++++++++++++++++++++++--------- ext/json11/json11.hpp | 40 +++++++++-- 2 files changed, 155 insertions(+), 35 deletions(-) diff --git a/ext/json11/json11.cpp b/ext/json11/json11.cpp index c448ae7e3..c08698898 100644 --- a/ext/json11/json11.cpp +++ b/ext/json11/json11.cpp @@ -21,10 +21,10 @@ #include "json11.hpp" #include +#include #include #include #include -#include namespace json11 { @@ -37,18 +37,31 @@ using std::make_shared; using std::initializer_list; using std::move; +/* Helper for representing null - just a do-nothing struct, plus comparison + * operators so the helpers in JsonValue work. We can't use nullptr_t because + * it may not be orderable. + */ +struct NullStruct { + bool operator==(NullStruct) const { return true; } + bool operator<(NullStruct) const { return false; } +}; + /* * * * * * * * * * * * * * * * * * * * * Serialization */ -static void dump(std::nullptr_t, string &out) { +static void dump(NullStruct, string &out) { out += "null"; } static void dump(double value, string &out) { - char buf[32]; - snprintf(buf, sizeof buf, "%.17g", value); - out += buf; + if (std::isfinite(value)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } else { + out += "null"; + } } static void dump(int value, string &out) { @@ -204,9 +217,9 @@ public: explicit JsonObject(Json::object &&value) : Value(move(value)) {} }; -class JsonNull final : public Value { +class JsonNull final : public Value { public: - JsonNull() : Value(nullptr) {} + JsonNull() : Value({}) {} }; /* * * * * * * * * * * * * * * * * * * * @@ -222,12 +235,12 @@ struct Statics { Statics() {} }; -const Statics & statics() { +static const Statics & statics() { static const Statics s {}; return s; } -const Json & static_null() { +static const Json & static_null() { // This has to be separate, not in Statics, because Json() accesses statics().null. static const Json json_null; return json_null; @@ -322,11 +335,12 @@ static inline bool in_range(long x, long lower, long upper) { return (x >= lower && x <= upper); } +namespace { /* JsonParser * * Object that tracks all state of an in-progress parse. */ -struct JsonParser { +struct JsonParser final { /* State */ @@ -334,6 +348,7 @@ struct JsonParser { size_t i; string &err; bool failed; + const JsonParse strategy; /* fail(msg, err_ret = Json()) * @@ -360,15 +375,76 @@ struct JsonParser { i++; } + /* consume_comment() + * + * Advance comments (c-style inline and multiline). + */ + bool consume_comment() { + bool comment_found = false; + if (str[i] == '/') { + i++; + if (i == str.size()) + return fail("unexpected end of input inside comment", false); + if (str[i] == '/') { // inline comment + i++; + if (i == str.size()) + return fail("unexpected end of input inside inline comment", false); + // advance until next line + while (str[i] != '\n') { + i++; + if (i == str.size()) + return fail("unexpected end of input inside inline comment", false); + } + comment_found = true; + } + else if (str[i] == '*') { // multiline comment + i++; + if (i > str.size()-2) + return fail("unexpected end of input inside multi-line comment", false); + // advance until closing tokens + while (!(str[i] == '*' && str[i+1] == '/')) { + i++; + if (i > str.size()-2) + return fail( + "unexpected end of input inside multi-line comment", false); + } + i += 2; + if (i == str.size()) + return fail( + "unexpected end of input inside multi-line comment", false); + comment_found = true; + } + else + return fail("malformed comment", false); + } + return comment_found; + } + + /* consume_garbage() + * + * Advance until the current character is non-whitespace and non-comment. + */ + void consume_garbage() { + consume_whitespace(); + if(strategy == JsonParse::COMMENTS) { + bool comment_found = false; + do { + comment_found = consume_comment(); + consume_whitespace(); + } + while(comment_found); + } + } + /* get_next_token() * * Return the next non-whitespace character. If the end of the input is reached, * flag an error and return 0. */ char get_next_token() { - consume_whitespace(); + consume_garbage(); if (i == str.size()) - return fail("unexpected end of input", 0); + return fail("unexpected end of input", (char)0); return str[i++]; } @@ -382,19 +458,19 @@ struct JsonParser { return; if (pt < 0x80) { - out += pt; + out += static_cast(pt); } else if (pt < 0x800) { - out += (pt >> 6) | 0xC0; - out += (pt & 0x3F) | 0x80; + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); } else if (pt < 0x10000) { - out += (pt >> 12) | 0xE0; - out += ((pt >> 6) & 0x3F) | 0x80; - out += (pt & 0x3F) | 0x80; + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); } else { - out += (pt >> 18) | 0xF0; - out += ((pt >> 12) & 0x3F) | 0x80; - out += ((pt >> 6) & 0x3F) | 0x80; - out += (pt & 0x3F) | 0x80; + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); } } @@ -436,7 +512,13 @@ struct JsonParser { if (ch == 'u') { // Extract 4-byte escape sequence string esc = str.substr(i, 4); - for (int j = 0; j < 4; j++) { + // Explicitly check length of the substring. The following loop + // relies on std::string returning the terminating NUL when + // accessing str[length]. Checking here reduces brittleness. + if (esc.length() < 4) { + return fail("bad \\u escape: " + esc, ""); + } + for (size_t j = 0; j < 4; j++) { if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') && !in_range(esc[j], '0', '9')) return fail("bad \\u escape: " + esc, ""); @@ -537,7 +619,7 @@ struct JsonParser { i++; } - return std::atof(str.c_str() + start_pos); + return std::strtod(str.c_str() + start_pos, nullptr); } /* expect(str, res) @@ -646,13 +728,14 @@ struct JsonParser { return fail("expected value, got " + esc(ch)); } }; +}//namespace { -Json Json::parse(const string &in, string &err) { - JsonParser parser { in, 0, err, false }; +Json Json::parse(const string &in, string &err, JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; Json result = parser.parse_json(0); // Check for any trailing garbage - parser.consume_whitespace(); + parser.consume_garbage(); if (parser.i != in.size()) return parser.fail("unexpected trailing " + esc(in[parser.i])); @@ -660,14 +743,19 @@ Json Json::parse(const string &in, string &err) { } // Documented in json11.hpp -vector Json::parse_multi(const string &in, string &err) { - JsonParser parser { in, 0, err, false }; - +vector Json::parse_multi(const string &in, + std::string::size_type &parser_stop_pos, + string &err, + JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + parser_stop_pos = 0; vector json_vec; while (parser.i != in.size() && !parser.failed) { json_vec.push_back(parser.parse_json(0)); // Check for another object - parser.consume_whitespace(); + parser.consume_garbage(); + if (!parser.failed) + parser_stop_pos = parser.i; } return json_vec; } diff --git a/ext/json11/json11.hpp b/ext/json11/json11.hpp index fe9bba405..5202ef932 100644 --- a/ext/json11/json11.hpp +++ b/ext/json11/json11.hpp @@ -56,8 +56,24 @@ #include #include +#ifdef _MSC_VER + #if _MSC_VER <= 1800 // VS 2013 + #ifndef noexcept + #define noexcept throw() + #endif + + #ifndef snprintf + #define snprintf _snprintf_s + #endif + #endif +#endif + namespace json11 { +enum JsonParse { + STANDARD, COMMENTS +}; + class JsonValue; class Json final { @@ -145,17 +161,33 @@ public: } // Parse. If parse fails, return Json() and assign an error message to err. - static Json parse(const std::string & in, std::string & err); - static Json parse(const char * in, std::string & err) { + static Json parse(const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + static Json parse(const char * in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { if (in) { - return parse(std::string(in), err); + return parse(std::string(in), err, strategy); } else { err = "null input"; return nullptr; } } // Parse multiple objects, concatenated or separated by whitespace - static std::vector parse_multi(const std::string & in, std::string & err); + static std::vector parse_multi( + const std::string & in, + std::string::size_type & parser_stop_pos, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector parse_multi( + const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + std::string::size_type parser_stop_pos; + return parse_multi(in, parser_stop_pos, err, strategy); + } bool operator== (const Json &rhs) const; bool operator< (const Json &rhs) const; -- 2.40.0