annotate json/json11/json11.cpp @ 66:6f160dee1192

Instead of using separate values and b64values entries in JSON serialisations, allow numeric arrays to be replaced by b64 variants wherever they appear (discriminating by type). Also rename values to featureValues in feature throughout, as values turns out to be a hazardous name in a JS context. Finally use Array instead of Text for array encoding (seems clearer).
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 27 Sep 2016 15:04:59 +0100
parents 6e8607ebad03
children
rev   line source
c@5 1 /* Copyright (c) 2013 Dropbox, Inc.
c@5 2 *
c@5 3 * Permission is hereby granted, free of charge, to any person obtaining a copy
c@5 4 * of this software and associated documentation files (the "Software"), to deal
c@5 5 * in the Software without restriction, including without limitation the rights
c@5 6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
c@5 7 * copies of the Software, and to permit persons to whom the Software is
c@5 8 * furnished to do so, subject to the following conditions:
c@5 9 *
c@5 10 * The above copyright notice and this permission notice shall be included in
c@5 11 * all copies or substantial portions of the Software.
c@5 12 *
c@5 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
c@5 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
c@5 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
c@5 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
c@5 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
c@5 18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
c@5 19 * THE SOFTWARE.
c@5 20 */
c@5 21
c@5 22 #include "json11.hpp"
c@5 23 #include <cassert>
c@5 24 #include <cmath>
c@5 25 #include <cstdlib>
c@5 26 #include <cstdio>
c@5 27 #include <limits>
c@5 28
c@5 29 namespace json11 {
c@5 30
c@5 31 static const int max_depth = 200;
c@5 32
c@5 33 using std::string;
c@5 34 using std::vector;
c@5 35 using std::map;
c@5 36 using std::make_shared;
c@5 37 using std::initializer_list;
c@5 38 using std::move;
c@5 39
c@5 40 /* * * * * * * * * * * * * * * * * * * *
c@5 41 * Serialization
c@5 42 */
c@5 43
c@5 44 static void dump(std::nullptr_t, string &out) {
c@5 45 out += "null";
c@5 46 }
c@5 47
c@5 48 static void dump(double value, string &out) {
c@5 49 if (std::isfinite(value)) {
c@5 50 char buf[32];
c@5 51 snprintf(buf, sizeof buf, "%.17g", value);
c@5 52 out += buf;
c@5 53 } else {
c@5 54 out += "null";
c@5 55 }
c@5 56 }
c@5 57
c@5 58 static void dump(int value, string &out) {
c@5 59 char buf[32];
c@5 60 snprintf(buf, sizeof buf, "%d", value);
c@5 61 out += buf;
c@5 62 }
c@5 63
c@5 64 static void dump(bool value, string &out) {
c@5 65 out += value ? "true" : "false";
c@5 66 }
c@5 67
c@5 68 static void dump(const string &value, string &out) {
c@5 69 out += '"';
c@5 70 for (size_t i = 0; i < value.length(); i++) {
c@5 71 const char ch = value[i];
c@5 72 if (ch == '\\') {
c@5 73 out += "\\\\";
c@5 74 } else if (ch == '"') {
c@5 75 out += "\\\"";
c@5 76 } else if (ch == '\b') {
c@5 77 out += "\\b";
c@5 78 } else if (ch == '\f') {
c@5 79 out += "\\f";
c@5 80 } else if (ch == '\n') {
c@5 81 out += "\\n";
c@5 82 } else if (ch == '\r') {
c@5 83 out += "\\r";
c@5 84 } else if (ch == '\t') {
c@5 85 out += "\\t";
c@5 86 } else if (static_cast<uint8_t>(ch) <= 0x1f) {
c@5 87 char buf[8];
c@5 88 snprintf(buf, sizeof buf, "\\u%04x", ch);
c@5 89 out += buf;
c@5 90 } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
c@5 91 && static_cast<uint8_t>(value[i+2]) == 0xa8) {
c@5 92 out += "\\u2028";
c@5 93 i += 2;
c@5 94 } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
c@5 95 && static_cast<uint8_t>(value[i+2]) == 0xa9) {
c@5 96 out += "\\u2029";
c@5 97 i += 2;
c@5 98 } else {
c@5 99 out += ch;
c@5 100 }
c@5 101 }
c@5 102 out += '"';
c@5 103 }
c@5 104
c@5 105 static void dump(const Json::array &values, string &out) {
c@5 106 bool first = true;
c@5 107 out += "[";
c@5 108 for (const auto &value : values) {
c@5 109 if (!first)
c@5 110 out += ", ";
c@5 111 value.dump(out);
c@5 112 first = false;
c@5 113 }
c@5 114 out += "]";
c@5 115 }
c@5 116
c@5 117 static void dump(const Json::object &values, string &out) {
c@5 118 bool first = true;
c@5 119 out += "{";
c@5 120 for (const auto &kv : values) {
c@5 121 if (!first)
c@5 122 out += ", ";
c@5 123 dump(kv.first, out);
c@5 124 out += ": ";
c@5 125 kv.second.dump(out);
c@5 126 first = false;
c@5 127 }
c@5 128 out += "}";
c@5 129 }
c@5 130
c@5 131 void Json::dump(string &out) const {
c@5 132 m_ptr->dump(out);
c@5 133 }
c@5 134
c@5 135 /* * * * * * * * * * * * * * * * * * * *
c@5 136 * Value wrappers
c@5 137 */
c@5 138
c@5 139 template <Json::Type tag, typename T>
c@5 140 class Value : public JsonValue {
c@5 141 protected:
c@5 142
c@5 143 // Constructors
c@5 144 explicit Value(const T &value) : m_value(value) {}
c@5 145 explicit Value(T &&value) : m_value(move(value)) {}
c@5 146
c@5 147 // Get type tag
c@5 148 Json::Type type() const override {
c@5 149 return tag;
c@5 150 }
c@5 151
c@5 152 // Comparisons
c@5 153 bool equals(const JsonValue * other) const override {
c@5 154 return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
c@5 155 }
c@5 156 bool less(const JsonValue * other) const override {
c@5 157 return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
c@5 158 }
c@5 159
c@5 160 const T m_value;
c@5 161 void dump(string &out) const override { json11::dump(m_value, out); }
c@5 162 };
c@5 163
c@5 164 class JsonDouble final : public Value<Json::NUMBER, double> {
c@5 165 double number_value() const override { return m_value; }
c@5 166 int int_value() const override { return static_cast<int>(m_value); }
c@5 167 bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
c@5 168 bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
c@5 169 public:
c@5 170 explicit JsonDouble(double value) : Value(value) {}
c@5 171 };
c@5 172
c@5 173 class JsonInt final : public Value<Json::NUMBER, int> {
c@5 174 double number_value() const override { return m_value; }
c@5 175 int int_value() const override { return m_value; }
c@5 176 bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
c@5 177 bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
c@5 178 public:
c@5 179 explicit JsonInt(int value) : Value(value) {}
c@5 180 };
c@5 181
c@5 182 class JsonBoolean final : public Value<Json::BOOL, bool> {
c@5 183 bool bool_value() const override { return m_value; }
c@5 184 public:
c@5 185 explicit JsonBoolean(bool value) : Value(value) {}
c@5 186 };
c@5 187
c@5 188 class JsonString final : public Value<Json::STRING, string> {
c@5 189 const string &string_value() const override { return m_value; }
c@5 190 public:
c@5 191 explicit JsonString(const string &value) : Value(value) {}
c@5 192 explicit JsonString(string &&value) : Value(move(value)) {}
c@5 193 };
c@5 194
c@5 195 class JsonArray final : public Value<Json::ARRAY, Json::array> {
c@5 196 const Json::array &array_items() const override { return m_value; }
c@5 197 const Json & operator[](size_t i) const override;
c@5 198 public:
c@5 199 explicit JsonArray(const Json::array &value) : Value(value) {}
c@5 200 explicit JsonArray(Json::array &&value) : Value(move(value)) {}
c@5 201 };
c@5 202
c@5 203 class JsonObject final : public Value<Json::OBJECT, Json::object> {
c@5 204 const Json::object &object_items() const override { return m_value; }
c@5 205 const Json & operator[](const string &key) const override;
c@5 206 public:
c@5 207 explicit JsonObject(const Json::object &value) : Value(value) {}
c@5 208 explicit JsonObject(Json::object &&value) : Value(move(value)) {}
c@5 209 };
c@5 210
c@5 211 class JsonNull final : public Value<Json::NUL, std::nullptr_t> {
c@5 212 public:
c@5 213 JsonNull() : Value(nullptr) {}
c@5 214 };
c@5 215
c@5 216 /* * * * * * * * * * * * * * * * * * * *
c@5 217 * Static globals - static-init-safe
c@5 218 */
c@5 219 struct Statics {
c@5 220 const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
c@5 221 const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
c@5 222 const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
c@5 223 const string empty_string;
c@5 224 const vector<Json> empty_vector;
c@5 225 const map<string, Json> empty_map;
c@5 226 Statics() {}
c@5 227 };
c@5 228
c@5 229 static const Statics & statics() {
c@5 230 static const Statics s {};
c@5 231 return s;
c@5 232 }
c@5 233
c@5 234 static const Json & static_null() {
c@5 235 // This has to be separate, not in Statics, because Json() accesses statics().null.
c@5 236 static const Json json_null;
c@5 237 return json_null;
c@5 238 }
c@5 239
c@5 240 /* * * * * * * * * * * * * * * * * * * *
c@5 241 * Constructors
c@5 242 */
c@5 243
c@5 244 Json::Json() noexcept : m_ptr(statics().null) {}
c@5 245 Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
c@5 246 Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
c@5 247 Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
c@5 248 Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
c@5 249 Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
c@5 250 Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
c@5 251 Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
c@5 252 Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
c@5 253 Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
c@5 254 Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
c@5 255 Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
c@5 256
c@5 257 /* * * * * * * * * * * * * * * * * * * *
c@5 258 * Accessors
c@5 259 */
c@5 260
c@5 261 Json::Type Json::type() const { return m_ptr->type(); }
c@5 262 double Json::number_value() const { return m_ptr->number_value(); }
c@5 263 int Json::int_value() const { return m_ptr->int_value(); }
c@5 264 bool Json::bool_value() const { return m_ptr->bool_value(); }
c@5 265 const string & Json::string_value() const { return m_ptr->string_value(); }
c@5 266 const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
c@5 267 const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
c@5 268 const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
c@5 269 const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
c@5 270
c@5 271 double JsonValue::number_value() const { return 0; }
c@5 272 int JsonValue::int_value() const { return 0; }
c@5 273 bool JsonValue::bool_value() const { return false; }
c@5 274 const string & JsonValue::string_value() const { return statics().empty_string; }
c@5 275 const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
c@5 276 const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
c@5 277 const Json & JsonValue::operator[] (size_t) const { return static_null(); }
c@5 278 const Json & JsonValue::operator[] (const string &) const { return static_null(); }
c@5 279
c@5 280 const Json & JsonObject::operator[] (const string &key) const {
c@5 281 auto iter = m_value.find(key);
c@5 282 return (iter == m_value.end()) ? static_null() : iter->second;
c@5 283 }
c@5 284 const Json & JsonArray::operator[] (size_t i) const {
c@5 285 if (i >= m_value.size()) return static_null();
c@5 286 else return m_value[i];
c@5 287 }
c@5 288
c@5 289 /* * * * * * * * * * * * * * * * * * * *
c@5 290 * Comparison
c@5 291 */
c@5 292
c@5 293 bool Json::operator== (const Json &other) const {
c@5 294 if (m_ptr->type() != other.m_ptr->type())
c@5 295 return false;
c@5 296
c@5 297 return m_ptr->equals(other.m_ptr.get());
c@5 298 }
c@5 299
c@5 300 bool Json::operator< (const Json &other) const {
c@5 301 if (m_ptr->type() != other.m_ptr->type())
c@5 302 return m_ptr->type() < other.m_ptr->type();
c@5 303
c@5 304 return m_ptr->less(other.m_ptr.get());
c@5 305 }
c@5 306
c@5 307 /* * * * * * * * * * * * * * * * * * * *
c@5 308 * Parsing
c@5 309 */
c@5 310
c@5 311 /* esc(c)
c@5 312 *
c@5 313 * Format char c suitable for printing in an error message.
c@5 314 */
c@5 315 static inline string esc(char c) {
c@5 316 char buf[12];
c@5 317 if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
c@5 318 snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
c@5 319 } else {
c@5 320 snprintf(buf, sizeof buf, "(%d)", c);
c@5 321 }
c@5 322 return string(buf);
c@5 323 }
c@5 324
c@5 325 static inline bool in_range(long x, long lower, long upper) {
c@5 326 return (x >= lower && x <= upper);
c@5 327 }
c@5 328
c@5 329 /* JsonParser
c@5 330 *
c@5 331 * Object that tracks all state of an in-progress parse.
c@5 332 */
c@5 333 struct JsonParser {
c@5 334
c@5 335 /* State
c@5 336 */
c@5 337 const string &str;
c@5 338 size_t i;
c@5 339 string &err;
c@5 340 bool failed;
c@5 341 const JsonParse strategy;
c@5 342
c@5 343 /* fail(msg, err_ret = Json())
c@5 344 *
c@5 345 * Mark this parse as failed.
c@5 346 */
c@5 347 Json fail(string &&msg) {
c@5 348 return fail(move(msg), Json());
c@5 349 }
c@5 350
c@5 351 template <typename T>
c@5 352 T fail(string &&msg, const T err_ret) {
c@5 353 if (!failed)
c@5 354 err = std::move(msg);
c@5 355 failed = true;
c@5 356 return err_ret;
c@5 357 }
c@5 358
c@5 359 /* consume_whitespace()
c@5 360 *
c@5 361 * Advance until the current character is non-whitespace.
c@5 362 */
c@5 363 void consume_whitespace() {
c@5 364 while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
c@5 365 i++;
c@5 366 }
c@5 367
c@5 368 /* consume_comment()
c@5 369 *
c@5 370 * Advance comments (c-style inline and multiline).
c@5 371 */
c@5 372 bool consume_comment() {
c@5 373 bool comment_found = false;
c@5 374 if (str[i] == '/') {
c@5 375 i++;
c@5 376 if (i == str.size())
c@5 377 return fail("unexpected end of input inside comment", 0);
c@5 378 if (str[i] == '/') { // inline comment
c@5 379 i++;
c@5 380 if (i == str.size())
c@5 381 return fail("unexpected end of input inside inline comment", 0);
c@5 382 // advance until next line
c@5 383 while (str[i] != '\n') {
c@5 384 i++;
c@5 385 if (i == str.size())
c@5 386 return fail("unexpected end of input inside inline comment", 0);
c@5 387 }
c@5 388 comment_found = true;
c@5 389 }
c@5 390 else if (str[i] == '*') { // multiline comment
c@5 391 i++;
c@5 392 if (i > str.size()-2)
c@5 393 return fail("unexpected end of input inside multi-line comment", 0);
c@5 394 // advance until closing tokens
c@5 395 while (!(str[i] == '*' && str[i+1] == '/')) {
c@5 396 i++;
c@5 397 if (i > str.size()-2)
c@5 398 return fail(
c@5 399 "unexpected end of input inside multi-line comment", 0);
c@5 400 }
c@5 401 i += 2;
c@5 402 if (i == str.size())
c@5 403 return fail(
c@5 404 "unexpected end of input inside multi-line comment", 0);
c@5 405 comment_found = true;
c@5 406 }
c@5 407 else
c@5 408 return fail("malformed comment", 0);
c@5 409 }
c@5 410 return comment_found;
c@5 411 }
c@5 412
c@5 413 /* consume_garbage()
c@5 414 *
c@5 415 * Advance until the current character is non-whitespace and non-comment.
c@5 416 */
c@5 417 void consume_garbage() {
c@5 418 consume_whitespace();
c@5 419 if(strategy == JsonParse::COMMENTS) {
c@5 420 bool comment_found = false;
c@5 421 do {
c@5 422 comment_found = consume_comment();
c@5 423 consume_whitespace();
c@5 424 }
c@5 425 while(comment_found);
c@5 426 }
c@5 427 }
c@5 428
c@5 429 /* get_next_token()
c@5 430 *
c@5 431 * Return the next non-whitespace character. If the end of the input is reached,
c@5 432 * flag an error and return 0.
c@5 433 */
c@5 434 char get_next_token() {
c@5 435 consume_garbage();
c@5 436 if (i == str.size())
c@5 437 return fail("unexpected end of input", 0);
c@5 438
c@5 439 return str[i++];
c@5 440 }
c@5 441
c@5 442 /* encode_utf8(pt, out)
c@5 443 *
c@5 444 * Encode pt as UTF-8 and add it to out.
c@5 445 */
c@5 446 void encode_utf8(long pt, string & out) {
c@5 447 if (pt < 0)
c@5 448 return;
c@5 449
c@5 450 if (pt < 0x80) {
c@5 451 out += static_cast<char>(pt);
c@5 452 } else if (pt < 0x800) {
c@5 453 out += static_cast<char>((pt >> 6) | 0xC0);
c@5 454 out += static_cast<char>((pt & 0x3F) | 0x80);
c@5 455 } else if (pt < 0x10000) {
c@5 456 out += static_cast<char>((pt >> 12) | 0xE0);
c@5 457 out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
c@5 458 out += static_cast<char>((pt & 0x3F) | 0x80);
c@5 459 } else {
c@5 460 out += static_cast<char>((pt >> 18) | 0xF0);
c@5 461 out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
c@5 462 out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
c@5 463 out += static_cast<char>((pt & 0x3F) | 0x80);
c@5 464 }
c@5 465 }
c@5 466
c@5 467 /* parse_string()
c@5 468 *
c@5 469 * Parse a string, starting at the current position.
c@5 470 */
c@5 471 string parse_string() {
c@5 472 string out;
c@5 473 long last_escaped_codepoint = -1;
c@5 474 while (true) {
c@5 475 if (i == str.size())
c@5 476 return fail("unexpected end of input in string", "");
c@5 477
c@5 478 char ch = str[i++];
c@5 479
c@5 480 if (ch == '"') {
c@5 481 encode_utf8(last_escaped_codepoint, out);
c@5 482 return out;
c@5 483 }
c@5 484
c@5 485 if (in_range(ch, 0, 0x1f))
c@5 486 return fail("unescaped " + esc(ch) + " in string", "");
c@5 487
c@5 488 // The usual case: non-escaped characters
c@5 489 if (ch != '\\') {
c@5 490 encode_utf8(last_escaped_codepoint, out);
c@5 491 last_escaped_codepoint = -1;
c@5 492 out += ch;
c@5 493 continue;
c@5 494 }
c@5 495
c@5 496 // Handle escapes
c@5 497 if (i == str.size())
c@5 498 return fail("unexpected end of input in string", "");
c@5 499
c@5 500 ch = str[i++];
c@5 501
c@5 502 if (ch == 'u') {
c@5 503 // Extract 4-byte escape sequence
c@5 504 string esc = str.substr(i, 4);
c@5 505 // Explicitly check length of the substring. The following loop
c@5 506 // relies on std::string returning the terminating NUL when
c@5 507 // accessing str[length]. Checking here reduces brittleness.
c@5 508 if (esc.length() < 4) {
c@5 509 return fail("bad \\u escape: " + esc, "");
c@5 510 }
c@5 511 for (int j = 0; j < 4; j++) {
c@5 512 if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
c@5 513 && !in_range(esc[j], '0', '9'))
c@5 514 return fail("bad \\u escape: " + esc, "");
c@5 515 }
c@5 516
c@5 517 long codepoint = strtol(esc.data(), nullptr, 16);
c@5 518
c@5 519 // JSON specifies that characters outside the BMP shall be encoded as a pair
c@5 520 // of 4-hex-digit \u escapes encoding their surrogate pair components. Check
c@5 521 // whether we're in the middle of such a beast: the previous codepoint was an
c@5 522 // escaped lead (high) surrogate, and this is a trail (low) surrogate.
c@5 523 if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
c@5 524 && in_range(codepoint, 0xDC00, 0xDFFF)) {
c@5 525 // Reassemble the two surrogate pairs into one astral-plane character, per
c@5 526 // the UTF-16 algorithm.
c@5 527 encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
c@5 528 | (codepoint - 0xDC00)) + 0x10000, out);
c@5 529 last_escaped_codepoint = -1;
c@5 530 } else {
c@5 531 encode_utf8(last_escaped_codepoint, out);
c@5 532 last_escaped_codepoint = codepoint;
c@5 533 }
c@5 534
c@5 535 i += 4;
c@5 536 continue;
c@5 537 }
c@5 538
c@5 539 encode_utf8(last_escaped_codepoint, out);
c@5 540 last_escaped_codepoint = -1;
c@5 541
c@5 542 if (ch == 'b') {
c@5 543 out += '\b';
c@5 544 } else if (ch == 'f') {
c@5 545 out += '\f';
c@5 546 } else if (ch == 'n') {
c@5 547 out += '\n';
c@5 548 } else if (ch == 'r') {
c@5 549 out += '\r';
c@5 550 } else if (ch == 't') {
c@5 551 out += '\t';
c@5 552 } else if (ch == '"' || ch == '\\' || ch == '/') {
c@5 553 out += ch;
c@5 554 } else {
c@5 555 return fail("invalid escape character " + esc(ch), "");
c@5 556 }
c@5 557 }
c@5 558 }
c@5 559
c@5 560 /* parse_number()
c@5 561 *
c@5 562 * Parse a double.
c@5 563 */
c@5 564 Json parse_number() {
c@5 565 size_t start_pos = i;
c@5 566
c@5 567 if (str[i] == '-')
c@5 568 i++;
c@5 569
c@5 570 // Integer part
c@5 571 if (str[i] == '0') {
c@5 572 i++;
c@5 573 if (in_range(str[i], '0', '9'))
c@5 574 return fail("leading 0s not permitted in numbers");
c@5 575 } else if (in_range(str[i], '1', '9')) {
c@5 576 i++;
c@5 577 while (in_range(str[i], '0', '9'))
c@5 578 i++;
c@5 579 } else {
c@5 580 return fail("invalid " + esc(str[i]) + " in number");
c@5 581 }
c@5 582
c@5 583 if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
c@5 584 && (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
c@5 585 return std::atoi(str.c_str() + start_pos);
c@5 586 }
c@5 587
c@5 588 // Decimal part
c@5 589 if (str[i] == '.') {
c@5 590 i++;
c@5 591 if (!in_range(str[i], '0', '9'))
c@5 592 return fail("at least one digit required in fractional part");
c@5 593
c@5 594 while (in_range(str[i], '0', '9'))
c@5 595 i++;
c@5 596 }
c@5 597
c@5 598 // Exponent part
c@5 599 if (str[i] == 'e' || str[i] == 'E') {
c@5 600 i++;
c@5 601
c@5 602 if (str[i] == '+' || str[i] == '-')
c@5 603 i++;
c@5 604
c@5 605 if (!in_range(str[i], '0', '9'))
c@5 606 return fail("at least one digit required in exponent");
c@5 607
c@5 608 while (in_range(str[i], '0', '9'))
c@5 609 i++;
c@5 610 }
c@5 611
c@5 612 return std::strtod(str.c_str() + start_pos, nullptr);
c@5 613 }
c@5 614
c@5 615 /* expect(str, res)
c@5 616 *
c@5 617 * Expect that 'str' starts at the character that was just read. If it does, advance
c@5 618 * the input and return res. If not, flag an error.
c@5 619 */
c@5 620 Json expect(const string &expected, Json res) {
c@5 621 assert(i != 0);
c@5 622 i--;
c@5 623 if (str.compare(i, expected.length(), expected) == 0) {
c@5 624 i += expected.length();
c@5 625 return res;
c@5 626 } else {
c@5 627 return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
c@5 628 }
c@5 629 }
c@5 630
c@5 631 /* parse_json()
c@5 632 *
c@5 633 * Parse a JSON object.
c@5 634 */
c@5 635 Json parse_json(int depth) {
c@5 636 if (depth > max_depth) {
c@5 637 return fail("exceeded maximum nesting depth");
c@5 638 }
c@5 639
c@5 640 char ch = get_next_token();
c@5 641 if (failed)
c@5 642 return Json();
c@5 643
c@5 644 if (ch == '-' || (ch >= '0' && ch <= '9')) {
c@5 645 i--;
c@5 646 return parse_number();
c@5 647 }
c@5 648
c@5 649 if (ch == 't')
c@5 650 return expect("true", true);
c@5 651
c@5 652 if (ch == 'f')
c@5 653 return expect("false", false);
c@5 654
c@5 655 if (ch == 'n')
c@5 656 return expect("null", Json());
c@5 657
c@5 658 if (ch == '"')
c@5 659 return parse_string();
c@5 660
c@5 661 if (ch == '{') {
c@5 662 map<string, Json> data;
c@5 663 ch = get_next_token();
c@5 664 if (ch == '}')
c@5 665 return data;
c@5 666
c@5 667 while (1) {
c@5 668 if (ch != '"')
c@5 669 return fail("expected '\"' in object, got " + esc(ch));
c@5 670
c@5 671 string key = parse_string();
c@5 672 if (failed)
c@5 673 return Json();
c@5 674
c@5 675 ch = get_next_token();
c@5 676 if (ch != ':')
c@5 677 return fail("expected ':' in object, got " + esc(ch));
c@5 678
c@5 679 data[std::move(key)] = parse_json(depth + 1);
c@5 680 if (failed)
c@5 681 return Json();
c@5 682
c@5 683 ch = get_next_token();
c@5 684 if (ch == '}')
c@5 685 break;
c@5 686 if (ch != ',')
c@5 687 return fail("expected ',' in object, got " + esc(ch));
c@5 688
c@5 689 ch = get_next_token();
c@5 690 }
c@5 691 return data;
c@5 692 }
c@5 693
c@5 694 if (ch == '[') {
c@5 695 vector<Json> data;
c@5 696 ch = get_next_token();
c@5 697 if (ch == ']')
c@5 698 return data;
c@5 699
c@5 700 while (1) {
c@5 701 i--;
c@5 702 data.push_back(parse_json(depth + 1));
c@5 703 if (failed)
c@5 704 return Json();
c@5 705
c@5 706 ch = get_next_token();
c@5 707 if (ch == ']')
c@5 708 break;
c@5 709 if (ch != ',')
c@5 710 return fail("expected ',' in list, got " + esc(ch));
c@5 711
c@5 712 ch = get_next_token();
c@5 713 (void)ch;
c@5 714 }
c@5 715 return data;
c@5 716 }
c@5 717
c@5 718 return fail("expected value, got " + esc(ch));
c@5 719 }
c@5 720 };
c@5 721
c@5 722 Json Json::parse(const string &in, string &err, JsonParse strategy) {
c@5 723 JsonParser parser { in, 0, err, false, strategy };
c@5 724 Json result = parser.parse_json(0);
c@5 725
c@5 726 // Check for any trailing garbage
c@5 727 parser.consume_garbage();
c@5 728 if (parser.i != in.size())
c@5 729 return parser.fail("unexpected trailing " + esc(in[parser.i]));
c@5 730
c@5 731 return result;
c@5 732 }
c@5 733
c@5 734 // Documented in json11.hpp
c@5 735 vector<Json> Json::parse_multi(const string &in,
c@5 736 string &err,
c@5 737 JsonParse strategy) {
c@5 738 JsonParser parser { in, 0, err, false, strategy };
c@5 739
c@5 740 vector<Json> json_vec;
c@5 741 while (parser.i != in.size() && !parser.failed) {
c@5 742 json_vec.push_back(parser.parse_json(0));
c@5 743 // Check for another object
c@5 744 parser.consume_garbage();
c@5 745 }
c@5 746 return json_vec;
c@5 747 }
c@5 748
c@5 749 /* * * * * * * * * * * * * * * * * * * *
c@5 750 * Shape-checking
c@5 751 */
c@5 752
c@5 753 bool Json::has_shape(const shape & types, string & err) const {
c@5 754 if (!is_object()) {
c@5 755 err = "expected JSON object, got " + dump();
c@5 756 return false;
c@5 757 }
c@5 758
c@5 759 for (auto & item : types) {
c@5 760 if ((*this)[item.first].type() != item.second) {
c@5 761 err = "bad type for " + item.first + " in " + dump();
c@5 762 return false;
c@5 763 }
c@5 764 }
c@5 765
c@5 766 return true;
c@5 767 }
c@5 768
c@5 769 } // namespace json11