Skip to content

Commit

Permalink
Backport json11 fixes from upstream
Browse files Browse the repository at this point in the history
(cherry picked from commit 3c20dd3)
  • Loading branch information
rgacogne authored and pieterlexis committed Feb 14, 2017
1 parent a08b422 commit 92901df
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 35 deletions.
150 changes: 119 additions & 31 deletions ext/json11/json11.cpp
Expand Up @@ -21,10 +21,10 @@

#include "json11.hpp"
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>
#include <string.h>

namespace json11 {

Expand All @@ -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) {
Expand Down Expand Up @@ -204,9 +217,9 @@ class JsonObject final : public Value<Json::OBJECT, Json::object> {
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({}) {}
};

/* * * * * * * * * * * * * * * * * * * *
Expand All @@ -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;
Expand Down Expand Up @@ -322,18 +335,20 @@ 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
*/
const string &str;
size_t i;
string &err;
bool failed;
const JsonParse strategy;

/* fail(msg, err_ret = Json())
*
Expand All @@ -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++];
}
Expand All @@ -382,19 +458,19 @@ struct JsonParser {
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);
}
}

Expand Down Expand Up @@ -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, "");
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -646,28 +728,34 @@ 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]));

return result;
}

// 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;
}
Expand Down
40 changes: 36 additions & 4 deletions ext/json11/json11.hpp
Expand Up @@ -56,8 +56,24 @@
#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 {
Expand Down Expand Up @@ -145,17 +161,33 @@ 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;
Expand Down

0 comments on commit 92901df

Please sign in to comment.