#include "json11.hpp"
#include <cassert>
+#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>
-#include <string.h>
namespace json11 {
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) {
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
};
-class JsonNull final : public Value<Json::NUL, std::nullptr_t> {
+class JsonNull final : public Value<Json::NUL, NullStruct> {
public:
- JsonNull() : Value(nullptr) {}
+ JsonNull() : Value({}) {}
};
/* * * * * * * * * * * * * * * * * * * *
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;
return (x >= lower && x <= upper);
}
+namespace {
/* JsonParser
*
* Object that tracks all state of an in-progress parse.
*/
-struct JsonParser {
+struct JsonParser final {
/* State
*/
size_t i;
string &err;
bool failed;
+ const JsonParse strategy;
/* fail(msg, err_ret = Json())
*
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++];
}
return;
if (pt < 0x80) {
- out += pt;
+ out += static_cast<char>(pt);
} else if (pt < 0x800) {
- out += (pt >> 6) | 0xC0;
- out += (pt & 0x3F) | 0x80;
+ out += static_cast<char>((pt >> 6) | 0xC0);
+ out += static_cast<char>((pt & 0x3F) | 0x80);
} else if (pt < 0x10000) {
- out += (pt >> 12) | 0xE0;
- out += ((pt >> 6) & 0x3F) | 0x80;
- out += (pt & 0x3F) | 0x80;
+ out += static_cast<char>((pt >> 12) | 0xE0);
+ out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
+ out += static_cast<char>((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<char>((pt >> 18) | 0xF0);
+ out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
+ out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
+ out += static_cast<char>((pt & 0x3F) | 0x80);
}
}
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, "");
i++;
}
- return std::atof(str.c_str() + start_pos);
+ return std::strtod(str.c_str() + start_pos, nullptr);
}
/* expect(str, res)
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]));
}
// Documented in json11.hpp
-vector<Json> Json::parse_multi(const string &in, string &err) {
- JsonParser parser { in, 0, err, false };
-
+vector<Json> 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> 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;
}
#include <memory>
#include <initializer_list>
+#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 {
}
// 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<Json> parse_multi(const std::string & in, std::string & err);
+ static std::vector<Json> parse_multi(
+ const std::string & in,
+ std::string::size_type & parser_stop_pos,
+ std::string & err,
+ JsonParse strategy = JsonParse::STANDARD);
+
+ static inline std::vector<Json> 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;