Path: blob/master/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
3158 views
// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors1// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.2// Distributed under MIT license, or public domain if desired and3// recognized in your jurisdiction.4// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE56#if !defined(JSON_IS_AMALGAMATION)7#include "json_tool.h"8#include <json/assertions.h>9#include <json/reader.h>10#include <json/value.h>11#endif // if !defined(JSON_IS_AMALGAMATION)12#include <algorithm>13#include <cassert>14#include <cmath>15#include <cstring>16#include <iostream>17#include <istream>18#include <limits>19#include <memory>20#include <set>21#include <sstream>22#include <utility>2324#include <cstdio>25#if __cplusplus >= 201103L2627#if !defined(sscanf)28#define sscanf std::sscanf29#endif3031#endif //__cplusplus3233#if defined(_MSC_VER)34#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)35#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 136#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES37#endif //_MSC_VER3839#if defined(_MSC_VER)40// Disable warning about strdup being deprecated.41#pragma warning(disable : 4996)42#endif4344// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile45// time to change the stack limit46#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)47#define JSONCPP_DEPRECATED_STACK_LIMIT 100048#endif4950static size_t const stackLimit_g =51JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()5253namespace Json {5455#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)56using CharReaderPtr = std::unique_ptr<CharReader>;57#else58using CharReaderPtr = std::auto_ptr<CharReader>;59#endif6061// Implementation of class Features62// ////////////////////////////////6364Features::Features() = default;6566Features Features::all() { return {}; }6768Features Features::strictMode() {69Features features;70features.allowComments_ = false;71features.strictRoot_ = true;72features.allowDroppedNullPlaceholders_ = false;73features.allowNumericKeys_ = false;74return features;75}7677// Implementation of class Reader78// ////////////////////////////////7980bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {81return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });82}8384// Class Reader85// //////////////////////////////////////////////////////////////////8687Reader::Reader() : features_(Features::all()) {}8889Reader::Reader(const Features& features) : features_(features) {}9091bool Reader::parse(const std::string& document, Value& root,92bool collectComments) {93document_.assign(document.begin(), document.end());94const char* begin = document_.c_str();95const char* end = begin + document_.length();96return parse(begin, end, root, collectComments);97}9899bool Reader::parse(std::istream& is, Value& root, bool collectComments) {100// std::istream_iterator<char> begin(is);101// std::istream_iterator<char> end;102// Those would allow streamed input from a file, if parse() were a103// template function.104105// Since String is reference-counted, this at least does not106// create an extra copy.107String doc(std::istreambuf_iterator<char>(is), {});108return parse(doc.data(), doc.data() + doc.size(), root, collectComments);109}110111bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,112bool collectComments) {113if (!features_.allowComments_) {114collectComments = false;115}116117begin_ = beginDoc;118end_ = endDoc;119collectComments_ = collectComments;120current_ = begin_;121lastValueEnd_ = nullptr;122lastValue_ = nullptr;123commentsBefore_.clear();124errors_.clear();125while (!nodes_.empty())126nodes_.pop();127nodes_.push(&root);128129bool successful = readValue();130Token token;131readTokenSkippingComments(token);132if (collectComments_ && !commentsBefore_.empty())133root.setComment(commentsBefore_, commentAfter);134if (features_.strictRoot_) {135if (!root.isArray() && !root.isObject()) {136// Set error location to start of doc, ideally should be first token found137// in doc138token.type_ = tokenError;139token.start_ = beginDoc;140token.end_ = endDoc;141addError(142"A valid JSON document must be either an array or an object value.",143token);144return false;145}146}147return successful;148}149150bool Reader::readValue() {151// readValue() may call itself only if it calls readObject() or ReadArray().152// These methods execute nodes_.push() just before and nodes_.pop)() just153// after calling readValue(). parse() executes one nodes_.push(), so > instead154// of >=.155if (nodes_.size() > stackLimit_g)156throwRuntimeError("Exceeded stackLimit in readValue().");157158Token token;159readTokenSkippingComments(token);160bool successful = true;161162if (collectComments_ && !commentsBefore_.empty()) {163currentValue().setComment(commentsBefore_, commentBefore);164commentsBefore_.clear();165}166167switch (token.type_) {168case tokenObjectBegin:169successful = readObject(token);170currentValue().setOffsetLimit(current_ - begin_);171break;172case tokenArrayBegin:173successful = readArray(token);174currentValue().setOffsetLimit(current_ - begin_);175break;176case tokenNumber:177successful = decodeNumber(token);178break;179case tokenString:180successful = decodeString(token);181break;182case tokenTrue: {183Value v(true);184currentValue().swapPayload(v);185currentValue().setOffsetStart(token.start_ - begin_);186currentValue().setOffsetLimit(token.end_ - begin_);187} break;188case tokenFalse: {189Value v(false);190currentValue().swapPayload(v);191currentValue().setOffsetStart(token.start_ - begin_);192currentValue().setOffsetLimit(token.end_ - begin_);193} break;194case tokenNull: {195Value v;196currentValue().swapPayload(v);197currentValue().setOffsetStart(token.start_ - begin_);198currentValue().setOffsetLimit(token.end_ - begin_);199} break;200case tokenArraySeparator:201case tokenObjectEnd:202case tokenArrayEnd:203if (features_.allowDroppedNullPlaceholders_) {204// "Un-read" the current token and mark the current value as a null205// token.206current_--;207Value v;208currentValue().swapPayload(v);209currentValue().setOffsetStart(current_ - begin_ - 1);210currentValue().setOffsetLimit(current_ - begin_);211break;212} // Else, fall through...213default:214currentValue().setOffsetStart(token.start_ - begin_);215currentValue().setOffsetLimit(token.end_ - begin_);216return addError("Syntax error: value, object or array expected.", token);217}218219if (collectComments_) {220lastValueEnd_ = current_;221lastValue_ = ¤tValue();222}223224return successful;225}226227bool Reader::readTokenSkippingComments(Token& token) {228bool success = readToken(token);229if (features_.allowComments_) {230while (success && token.type_ == tokenComment) {231success = readToken(token);232}233}234return success;235}236237bool Reader::readToken(Token& token) {238skipSpaces();239token.start_ = current_;240Char c = getNextChar();241bool ok = true;242switch (c) {243case '{':244token.type_ = tokenObjectBegin;245break;246case '}':247token.type_ = tokenObjectEnd;248break;249case '[':250token.type_ = tokenArrayBegin;251break;252case ']':253token.type_ = tokenArrayEnd;254break;255case '"':256token.type_ = tokenString;257ok = readString();258break;259case '/':260token.type_ = tokenComment;261ok = readComment();262break;263case '0':264case '1':265case '2':266case '3':267case '4':268case '5':269case '6':270case '7':271case '8':272case '9':273case '-':274token.type_ = tokenNumber;275readNumber();276break;277case 't':278token.type_ = tokenTrue;279ok = match("rue", 3);280break;281case 'f':282token.type_ = tokenFalse;283ok = match("alse", 4);284break;285case 'n':286token.type_ = tokenNull;287ok = match("ull", 3);288break;289case ',':290token.type_ = tokenArraySeparator;291break;292case ':':293token.type_ = tokenMemberSeparator;294break;295case 0:296token.type_ = tokenEndOfStream;297break;298default:299ok = false;300break;301}302if (!ok)303token.type_ = tokenError;304token.end_ = current_;305return ok;306}307308void Reader::skipSpaces() {309while (current_ != end_) {310Char c = *current_;311if (c == ' ' || c == '\t' || c == '\r' || c == '\n')312++current_;313else314break;315}316}317318bool Reader::match(const Char* pattern, int patternLength) {319if (end_ - current_ < patternLength)320return false;321int index = patternLength;322while (index--)323if (current_[index] != pattern[index])324return false;325current_ += patternLength;326return true;327}328329bool Reader::readComment() {330Location commentBegin = current_ - 1;331Char c = getNextChar();332bool successful = false;333if (c == '*')334successful = readCStyleComment();335else if (c == '/')336successful = readCppStyleComment();337if (!successful)338return false;339340if (collectComments_) {341CommentPlacement placement = commentBefore;342if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {343if (c != '*' || !containsNewLine(commentBegin, current_))344placement = commentAfterOnSameLine;345}346347addComment(commentBegin, current_, placement);348}349return true;350}351352String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {353String normalized;354normalized.reserve(static_cast<size_t>(end - begin));355Reader::Location current = begin;356while (current != end) {357char c = *current++;358if (c == '\r') {359if (current != end && *current == '\n')360// convert dos EOL361++current;362// convert Mac EOL363normalized += '\n';364} else {365normalized += c;366}367}368return normalized;369}370371void Reader::addComment(Location begin, Location end,372CommentPlacement placement) {373assert(collectComments_);374const String& normalized = normalizeEOL(begin, end);375if (placement == commentAfterOnSameLine) {376assert(lastValue_ != nullptr);377lastValue_->setComment(normalized, placement);378} else {379commentsBefore_ += normalized;380}381}382383bool Reader::readCStyleComment() {384while ((current_ + 1) < end_) {385Char c = getNextChar();386if (c == '*' && *current_ == '/')387break;388}389return getNextChar() == '/';390}391392bool Reader::readCppStyleComment() {393while (current_ != end_) {394Char c = getNextChar();395if (c == '\n')396break;397if (c == '\r') {398// Consume DOS EOL. It will be normalized in addComment.399if (current_ != end_ && *current_ == '\n')400getNextChar();401// Break on Moc OS 9 EOL.402break;403}404}405return true;406}407408void Reader::readNumber() {409Location p = current_;410char c = '0'; // stopgap for already consumed character411// integral part412while (c >= '0' && c <= '9')413c = (current_ = p) < end_ ? *p++ : '\0';414// fractional part415if (c == '.') {416c = (current_ = p) < end_ ? *p++ : '\0';417while (c >= '0' && c <= '9')418c = (current_ = p) < end_ ? *p++ : '\0';419}420// exponential part421if (c == 'e' || c == 'E') {422c = (current_ = p) < end_ ? *p++ : '\0';423if (c == '+' || c == '-')424c = (current_ = p) < end_ ? *p++ : '\0';425while (c >= '0' && c <= '9')426c = (current_ = p) < end_ ? *p++ : '\0';427}428}429430bool Reader::readString() {431Char c = '\0';432while (current_ != end_) {433c = getNextChar();434if (c == '\\')435getNextChar();436else if (c == '"')437break;438}439return c == '"';440}441442bool Reader::readObject(Token& token) {443Token tokenName;444String name;445Value init(objectValue);446currentValue().swapPayload(init);447currentValue().setOffsetStart(token.start_ - begin_);448while (readTokenSkippingComments(tokenName)) {449if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object450return true;451name.clear();452if (tokenName.type_ == tokenString) {453if (!decodeString(tokenName, name))454return recoverFromError(tokenObjectEnd);455} else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {456Value numberName;457if (!decodeNumber(tokenName, numberName))458return recoverFromError(tokenObjectEnd);459name = numberName.asString();460} else {461break;462}463464Token colon;465if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {466return addErrorAndRecover("Missing ':' after object member name", colon,467tokenObjectEnd);468}469Value& value = currentValue()[name];470nodes_.push(&value);471bool ok = readValue();472nodes_.pop();473if (!ok) // error already set474return recoverFromError(tokenObjectEnd);475476Token comma;477if (!readTokenSkippingComments(comma) ||478(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {479return addErrorAndRecover("Missing ',' or '}' in object declaration",480comma, tokenObjectEnd);481}482if (comma.type_ == tokenObjectEnd)483return true;484}485return addErrorAndRecover("Missing '}' or object member name", tokenName,486tokenObjectEnd);487}488489bool Reader::readArray(Token& token) {490Value init(arrayValue);491currentValue().swapPayload(init);492currentValue().setOffsetStart(token.start_ - begin_);493skipSpaces();494if (current_ != end_ && *current_ == ']') // empty array495{496Token endArray;497readToken(endArray);498return true;499}500int index = 0;501for (;;) {502Value& value = currentValue()[index++];503nodes_.push(&value);504bool ok = readValue();505nodes_.pop();506if (!ok) // error already set507return recoverFromError(tokenArrayEnd);508509Token currentToken;510// Accept Comment after last item in the array.511ok = readTokenSkippingComments(currentToken);512bool badTokenType = (currentToken.type_ != tokenArraySeparator &&513currentToken.type_ != tokenArrayEnd);514if (!ok || badTokenType) {515return addErrorAndRecover("Missing ',' or ']' in array declaration",516currentToken, tokenArrayEnd);517}518if (currentToken.type_ == tokenArrayEnd)519break;520}521return true;522}523524bool Reader::decodeNumber(Token& token) {525Value decoded;526if (!decodeNumber(token, decoded))527return false;528currentValue().swapPayload(decoded);529currentValue().setOffsetStart(token.start_ - begin_);530currentValue().setOffsetLimit(token.end_ - begin_);531return true;532}533534bool Reader::decodeNumber(Token& token, Value& decoded) {535// Attempts to parse the number as an integer. If the number is536// larger than the maximum supported value of an integer then537// we decode the number as a double.538Location current = token.start_;539bool isNegative = *current == '-';540if (isNegative)541++current;542// TODO: Help the compiler do the div and mod at compile time or get rid of543// them.544Value::LargestUInt maxIntegerValue =545isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1546: Value::maxLargestUInt;547Value::LargestUInt threshold = maxIntegerValue / 10;548Value::LargestUInt value = 0;549while (current < token.end_) {550Char c = *current++;551if (c < '0' || c > '9')552return decodeDouble(token, decoded);553auto digit(static_cast<Value::UInt>(c - '0'));554if (value >= threshold) {555// We've hit or exceeded the max value divided by 10 (rounded down). If556// a) we've only just touched the limit, b) this is the last digit, and557// c) it's small enough to fit in that rounding delta, we're okay.558// Otherwise treat this number as a double to avoid overflow.559if (value > threshold || current != token.end_ ||560digit > maxIntegerValue % 10) {561return decodeDouble(token, decoded);562}563}564value = value * 10 + digit;565}566if (isNegative && value == maxIntegerValue)567decoded = Value::minLargestInt;568else if (isNegative)569decoded = -Value::LargestInt(value);570else if (value <= Value::LargestUInt(Value::maxInt))571decoded = Value::LargestInt(value);572else573decoded = value;574return true;575}576577bool Reader::decodeDouble(Token& token) {578Value decoded;579if (!decodeDouble(token, decoded))580return false;581currentValue().swapPayload(decoded);582currentValue().setOffsetStart(token.start_ - begin_);583currentValue().setOffsetLimit(token.end_ - begin_);584return true;585}586587bool Reader::decodeDouble(Token& token, Value& decoded) {588double value = 0;589IStringStream is(String(token.start_, token.end_));590if (!(is >> value)) {591if (value == std::numeric_limits<double>::max())592value = std::numeric_limits<double>::infinity();593else if (value == std::numeric_limits<double>::lowest())594value = -std::numeric_limits<double>::infinity();595else if (!std::isinf(value))596return addError(597"'" + String(token.start_, token.end_) + "' is not a number.", token);598}599decoded = value;600return true;601}602603bool Reader::decodeString(Token& token) {604String decoded_string;605if (!decodeString(token, decoded_string))606return false;607Value decoded(decoded_string);608currentValue().swapPayload(decoded);609currentValue().setOffsetStart(token.start_ - begin_);610currentValue().setOffsetLimit(token.end_ - begin_);611return true;612}613614bool Reader::decodeString(Token& token, String& decoded) {615decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));616Location current = token.start_ + 1; // skip '"'617Location end = token.end_ - 1; // do not include '"'618while (current != end) {619Char c = *current++;620if (c == '"')621break;622if (c == '\\') {623if (current == end)624return addError("Empty escape sequence in string", token, current);625Char escape = *current++;626switch (escape) {627case '"':628decoded += '"';629break;630case '/':631decoded += '/';632break;633case '\\':634decoded += '\\';635break;636case 'b':637decoded += '\b';638break;639case 'f':640decoded += '\f';641break;642case 'n':643decoded += '\n';644break;645case 'r':646decoded += '\r';647break;648case 't':649decoded += '\t';650break;651case 'u': {652unsigned int unicode;653if (!decodeUnicodeCodePoint(token, current, end, unicode))654return false;655decoded += codePointToUTF8(unicode);656} break;657default:658return addError("Bad escape sequence in string", token, current);659}660} else {661decoded += c;662}663}664return true;665}666667bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,668Location end, unsigned int& unicode) {669670if (!decodeUnicodeEscapeSequence(token, current, end, unicode))671return false;672if (unicode >= 0xD800 && unicode <= 0xDBFF) {673// surrogate pairs674if (end - current < 6)675return addError(676"additional six characters expected to parse unicode surrogate pair.",677token, current);678if (*(current++) == '\\' && *(current++) == 'u') {679unsigned int surrogatePair;680if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {681unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);682} else683return false;684} else685return addError("expecting another \\u token to begin the second half of "686"a unicode surrogate pair",687token, current);688}689return true;690}691692bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,693Location end,694unsigned int& ret_unicode) {695if (end - current < 4)696return addError(697"Bad unicode escape sequence in string: four digits expected.", token,698current);699int unicode = 0;700for (int index = 0; index < 4; ++index) {701Char c = *current++;702unicode *= 16;703if (c >= '0' && c <= '9')704unicode += c - '0';705else if (c >= 'a' && c <= 'f')706unicode += c - 'a' + 10;707else if (c >= 'A' && c <= 'F')708unicode += c - 'A' + 10;709else710return addError(711"Bad unicode escape sequence in string: hexadecimal digit expected.",712token, current);713}714ret_unicode = static_cast<unsigned int>(unicode);715return true;716}717718bool Reader::addError(const String& message, Token& token, Location extra) {719ErrorInfo info;720info.token_ = token;721info.message_ = message;722info.extra_ = extra;723errors_.push_back(info);724return false;725}726727bool Reader::recoverFromError(TokenType skipUntilToken) {728size_t const errorCount = errors_.size();729Token skip;730for (;;) {731if (!readToken(skip))732errors_.resize(errorCount); // discard errors caused by recovery733if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)734break;735}736errors_.resize(errorCount);737return false;738}739740bool Reader::addErrorAndRecover(const String& message, Token& token,741TokenType skipUntilToken) {742addError(message, token);743return recoverFromError(skipUntilToken);744}745746Value& Reader::currentValue() { return *(nodes_.top()); }747748Reader::Char Reader::getNextChar() {749if (current_ == end_)750return 0;751return *current_++;752}753754void Reader::getLocationLineAndColumn(Location location, int& line,755int& column) const {756Location current = begin_;757Location lastLineStart = current;758line = 0;759while (current < location && current != end_) {760Char c = *current++;761if (c == '\r') {762if (current != end_ && *current == '\n')763++current;764lastLineStart = current;765++line;766} else if (c == '\n') {767lastLineStart = current;768++line;769}770}771// column & line start at 1772column = int(location - lastLineStart) + 1;773++line;774}775776String Reader::getLocationLineAndColumn(Location location) const {777int line, column;778getLocationLineAndColumn(location, line, column);779char buffer[18 + 16 + 16 + 1];780jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);781return buffer;782}783784// Deprecated. Preserved for backward compatibility785String Reader::getFormatedErrorMessages() const {786return getFormattedErrorMessages();787}788789String Reader::getFormattedErrorMessages() const {790String formattedMessage;791for (const auto& error : errors_) {792formattedMessage +=793"* " + getLocationLineAndColumn(error.token_.start_) + "\n";794formattedMessage += " " + error.message_ + "\n";795if (error.extra_)796formattedMessage +=797"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";798}799return formattedMessage;800}801802std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {803std::vector<Reader::StructuredError> allErrors;804for (const auto& error : errors_) {805Reader::StructuredError structured;806structured.offset_start = error.token_.start_ - begin_;807structured.offset_limit = error.token_.end_ - begin_;808structured.message = error.message_;809allErrors.push_back(structured);810}811return allErrors;812}813814bool Reader::pushError(const Value& value, const String& message) {815ptrdiff_t const length = end_ - begin_;816if (value.getOffsetStart() > length || value.getOffsetLimit() > length)817return false;818Token token;819token.type_ = tokenError;820token.start_ = begin_ + value.getOffsetStart();821token.end_ = begin_ + value.getOffsetLimit();822ErrorInfo info;823info.token_ = token;824info.message_ = message;825info.extra_ = nullptr;826errors_.push_back(info);827return true;828}829830bool Reader::pushError(const Value& value, const String& message,831const Value& extra) {832ptrdiff_t const length = end_ - begin_;833if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||834extra.getOffsetLimit() > length)835return false;836Token token;837token.type_ = tokenError;838token.start_ = begin_ + value.getOffsetStart();839token.end_ = begin_ + value.getOffsetLimit();840ErrorInfo info;841info.token_ = token;842info.message_ = message;843info.extra_ = begin_ + extra.getOffsetStart();844errors_.push_back(info);845return true;846}847848bool Reader::good() const { return errors_.empty(); }849850// Originally copied from the Features class (now deprecated), used internally851// for features implementation.852class OurFeatures {853public:854static OurFeatures all();855bool allowComments_;856bool allowTrailingCommas_;857bool strictRoot_;858bool allowDroppedNullPlaceholders_;859bool allowNumericKeys_;860bool allowSingleQuotes_;861bool failIfExtra_;862bool rejectDupKeys_;863bool allowSpecialFloats_;864bool skipBom_;865size_t stackLimit_;866}; // OurFeatures867868OurFeatures OurFeatures::all() { return {}; }869870// Implementation of class Reader871// ////////////////////////////////872873// Originally copied from the Reader class (now deprecated), used internally874// for implementing JSON reading.875class OurReader {876public:877using Char = char;878using Location = const Char*;879880explicit OurReader(OurFeatures const& features);881bool parse(const char* beginDoc, const char* endDoc, Value& root,882bool collectComments = true);883String getFormattedErrorMessages() const;884std::vector<CharReader::StructuredError> getStructuredErrors() const;885886private:887OurReader(OurReader const&); // no impl888void operator=(OurReader const&); // no impl889890enum TokenType {891tokenEndOfStream = 0,892tokenObjectBegin,893tokenObjectEnd,894tokenArrayBegin,895tokenArrayEnd,896tokenString,897tokenNumber,898tokenTrue,899tokenFalse,900tokenNull,901tokenNaN,902tokenPosInf,903tokenNegInf,904tokenArraySeparator,905tokenMemberSeparator,906tokenComment,907tokenError908};909910class Token {911public:912TokenType type_;913Location start_;914Location end_;915};916917class ErrorInfo {918public:919Token token_;920String message_;921Location extra_;922};923924using Errors = std::deque<ErrorInfo>;925926bool readToken(Token& token);927bool readTokenSkippingComments(Token& token);928void skipSpaces();929void skipBom(bool skipBom);930bool match(const Char* pattern, int patternLength);931bool readComment();932bool readCStyleComment(bool* containsNewLineResult);933bool readCppStyleComment();934bool readString();935bool readStringSingleQuote();936bool readNumber(bool checkInf);937bool readValue();938bool readObject(Token& token);939bool readArray(Token& token);940bool decodeNumber(Token& token);941bool decodeNumber(Token& token, Value& decoded);942bool decodeString(Token& token);943bool decodeString(Token& token, String& decoded);944bool decodeDouble(Token& token);945bool decodeDouble(Token& token, Value& decoded);946bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,947unsigned int& unicode);948bool decodeUnicodeEscapeSequence(Token& token, Location& current,949Location end, unsigned int& unicode);950bool addError(const String& message, Token& token, Location extra = nullptr);951bool recoverFromError(TokenType skipUntilToken);952bool addErrorAndRecover(const String& message, Token& token,953TokenType skipUntilToken);954void skipUntilSpace();955Value& currentValue();956Char getNextChar();957void getLocationLineAndColumn(Location location, int& line,958int& column) const;959String getLocationLineAndColumn(Location location) const;960void addComment(Location begin, Location end, CommentPlacement placement);961962static String normalizeEOL(Location begin, Location end);963static bool containsNewLine(Location begin, Location end);964965using Nodes = std::stack<Value*>;966967Nodes nodes_{};968Errors errors_{};969String document_{};970Location begin_ = nullptr;971Location end_ = nullptr;972Location current_ = nullptr;973Location lastValueEnd_ = nullptr;974Value* lastValue_ = nullptr;975bool lastValueHasAComment_ = false;976String commentsBefore_{};977978OurFeatures const features_;979bool collectComments_ = false;980}; // OurReader981982// complete copy of Read impl, for OurReader983984bool OurReader::containsNewLine(OurReader::Location begin,985OurReader::Location end) {986return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });987}988989OurReader::OurReader(OurFeatures const& features) : features_(features) {}990991bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,992bool collectComments) {993if (!features_.allowComments_) {994collectComments = false;995}996997begin_ = beginDoc;998end_ = endDoc;999collectComments_ = collectComments;1000current_ = begin_;1001lastValueEnd_ = nullptr;1002lastValue_ = nullptr;1003commentsBefore_.clear();1004errors_.clear();1005while (!nodes_.empty())1006nodes_.pop();1007nodes_.push(&root);10081009// skip byte order mark if it exists at the beginning of the UTF-8 text.1010skipBom(features_.skipBom_);1011bool successful = readValue();1012nodes_.pop();1013Token token;1014readTokenSkippingComments(token);1015if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {1016addError("Extra non-whitespace after JSON value.", token);1017return false;1018}1019if (collectComments_ && !commentsBefore_.empty())1020root.setComment(commentsBefore_, commentAfter);1021if (features_.strictRoot_) {1022if (!root.isArray() && !root.isObject()) {1023// Set error location to start of doc, ideally should be first token found1024// in doc1025token.type_ = tokenError;1026token.start_ = beginDoc;1027token.end_ = endDoc;1028addError(1029"A valid JSON document must be either an array or an object value.",1030token);1031return false;1032}1033}1034return successful;1035}10361037bool OurReader::readValue() {1038// To preserve the old behaviour we cast size_t to int.1039if (nodes_.size() > features_.stackLimit_)1040throwRuntimeError("Exceeded stackLimit in readValue().");1041Token token;1042readTokenSkippingComments(token);1043bool successful = true;10441045if (collectComments_ && !commentsBefore_.empty()) {1046currentValue().setComment(commentsBefore_, commentBefore);1047commentsBefore_.clear();1048}10491050switch (token.type_) {1051case tokenObjectBegin:1052successful = readObject(token);1053currentValue().setOffsetLimit(current_ - begin_);1054break;1055case tokenArrayBegin:1056successful = readArray(token);1057currentValue().setOffsetLimit(current_ - begin_);1058break;1059case tokenNumber:1060successful = decodeNumber(token);1061break;1062case tokenString:1063successful = decodeString(token);1064break;1065case tokenTrue: {1066Value v(true);1067currentValue().swapPayload(v);1068currentValue().setOffsetStart(token.start_ - begin_);1069currentValue().setOffsetLimit(token.end_ - begin_);1070} break;1071case tokenFalse: {1072Value v(false);1073currentValue().swapPayload(v);1074currentValue().setOffsetStart(token.start_ - begin_);1075currentValue().setOffsetLimit(token.end_ - begin_);1076} break;1077case tokenNull: {1078Value v;1079currentValue().swapPayload(v);1080currentValue().setOffsetStart(token.start_ - begin_);1081currentValue().setOffsetLimit(token.end_ - begin_);1082} break;1083case tokenNaN: {1084Value v(std::numeric_limits<double>::quiet_NaN());1085currentValue().swapPayload(v);1086currentValue().setOffsetStart(token.start_ - begin_);1087currentValue().setOffsetLimit(token.end_ - begin_);1088} break;1089case tokenPosInf: {1090Value v(std::numeric_limits<double>::infinity());1091currentValue().swapPayload(v);1092currentValue().setOffsetStart(token.start_ - begin_);1093currentValue().setOffsetLimit(token.end_ - begin_);1094} break;1095case tokenNegInf: {1096Value v(-std::numeric_limits<double>::infinity());1097currentValue().swapPayload(v);1098currentValue().setOffsetStart(token.start_ - begin_);1099currentValue().setOffsetLimit(token.end_ - begin_);1100} break;1101case tokenArraySeparator:1102case tokenObjectEnd:1103case tokenArrayEnd:1104if (features_.allowDroppedNullPlaceholders_) {1105// "Un-read" the current token and mark the current value as a null1106// token.1107current_--;1108Value v;1109currentValue().swapPayload(v);1110currentValue().setOffsetStart(current_ - begin_ - 1);1111currentValue().setOffsetLimit(current_ - begin_);1112break;1113} // else, fall through ...1114default:1115currentValue().setOffsetStart(token.start_ - begin_);1116currentValue().setOffsetLimit(token.end_ - begin_);1117return addError("Syntax error: value, object or array expected.", token);1118}11191120if (collectComments_) {1121lastValueEnd_ = current_;1122lastValueHasAComment_ = false;1123lastValue_ = ¤tValue();1124}11251126return successful;1127}11281129bool OurReader::readTokenSkippingComments(Token& token) {1130bool success = readToken(token);1131if (features_.allowComments_) {1132while (success && token.type_ == tokenComment) {1133success = readToken(token);1134}1135}1136return success;1137}11381139bool OurReader::readToken(Token& token) {1140skipSpaces();1141token.start_ = current_;1142Char c = getNextChar();1143bool ok = true;1144switch (c) {1145case '{':1146token.type_ = tokenObjectBegin;1147break;1148case '}':1149token.type_ = tokenObjectEnd;1150break;1151case '[':1152token.type_ = tokenArrayBegin;1153break;1154case ']':1155token.type_ = tokenArrayEnd;1156break;1157case '"':1158token.type_ = tokenString;1159ok = readString();1160break;1161case '\'':1162if (features_.allowSingleQuotes_) {1163token.type_ = tokenString;1164ok = readStringSingleQuote();1165} else {1166// If we don't allow single quotes, this is a failure case.1167ok = false;1168}1169break;1170case '/':1171token.type_ = tokenComment;1172ok = readComment();1173break;1174case '0':1175case '1':1176case '2':1177case '3':1178case '4':1179case '5':1180case '6':1181case '7':1182case '8':1183case '9':1184token.type_ = tokenNumber;1185readNumber(false);1186break;1187case '-':1188if (readNumber(true)) {1189token.type_ = tokenNumber;1190} else {1191token.type_ = tokenNegInf;1192ok = features_.allowSpecialFloats_ && match("nfinity", 7);1193}1194break;1195case '+':1196if (readNumber(true)) {1197token.type_ = tokenNumber;1198} else {1199token.type_ = tokenPosInf;1200ok = features_.allowSpecialFloats_ && match("nfinity", 7);1201}1202break;1203case 't':1204token.type_ = tokenTrue;1205ok = match("rue", 3);1206break;1207case 'f':1208token.type_ = tokenFalse;1209ok = match("alse", 4);1210break;1211case 'n':1212token.type_ = tokenNull;1213ok = match("ull", 3);1214break;1215case 'N':1216if (features_.allowSpecialFloats_) {1217token.type_ = tokenNaN;1218ok = match("aN", 2);1219} else {1220ok = false;1221}1222break;1223case 'I':1224if (features_.allowSpecialFloats_) {1225token.type_ = tokenPosInf;1226ok = match("nfinity", 7);1227} else {1228ok = false;1229}1230break;1231case ',':1232token.type_ = tokenArraySeparator;1233break;1234case ':':1235token.type_ = tokenMemberSeparator;1236break;1237case 0:1238token.type_ = tokenEndOfStream;1239break;1240default:1241ok = false;1242break;1243}1244if (!ok)1245token.type_ = tokenError;1246token.end_ = current_;1247return ok;1248}12491250void OurReader::skipSpaces() {1251while (current_ != end_) {1252Char c = *current_;1253if (c == ' ' || c == '\t' || c == '\r' || c == '\n')1254++current_;1255else1256break;1257}1258}12591260void OurReader::skipBom(bool skipBom) {1261// The default behavior is to skip BOM.1262if (skipBom) {1263if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {1264begin_ += 3;1265current_ = begin_;1266}1267}1268}12691270bool OurReader::match(const Char* pattern, int patternLength) {1271if (end_ - current_ < patternLength)1272return false;1273int index = patternLength;1274while (index--)1275if (current_[index] != pattern[index])1276return false;1277current_ += patternLength;1278return true;1279}12801281bool OurReader::readComment() {1282const Location commentBegin = current_ - 1;1283const Char c = getNextChar();1284bool successful = false;1285bool cStyleWithEmbeddedNewline = false;12861287const bool isCStyleComment = (c == '*');1288const bool isCppStyleComment = (c == '/');1289if (isCStyleComment) {1290successful = readCStyleComment(&cStyleWithEmbeddedNewline);1291} else if (isCppStyleComment) {1292successful = readCppStyleComment();1293}12941295if (!successful)1296return false;12971298if (collectComments_) {1299CommentPlacement placement = commentBefore;13001301if (!lastValueHasAComment_) {1302if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {1303if (isCppStyleComment || !cStyleWithEmbeddedNewline) {1304placement = commentAfterOnSameLine;1305lastValueHasAComment_ = true;1306}1307}1308}13091310addComment(commentBegin, current_, placement);1311}1312return true;1313}13141315String OurReader::normalizeEOL(OurReader::Location begin,1316OurReader::Location end) {1317String normalized;1318normalized.reserve(static_cast<size_t>(end - begin));1319OurReader::Location current = begin;1320while (current != end) {1321char c = *current++;1322if (c == '\r') {1323if (current != end && *current == '\n')1324// convert dos EOL1325++current;1326// convert Mac EOL1327normalized += '\n';1328} else {1329normalized += c;1330}1331}1332return normalized;1333}13341335void OurReader::addComment(Location begin, Location end,1336CommentPlacement placement) {1337assert(collectComments_);1338const String& normalized = normalizeEOL(begin, end);1339if (placement == commentAfterOnSameLine) {1340assert(lastValue_ != nullptr);1341lastValue_->setComment(normalized, placement);1342} else {1343commentsBefore_ += normalized;1344}1345}13461347bool OurReader::readCStyleComment(bool* containsNewLineResult) {1348*containsNewLineResult = false;13491350while ((current_ + 1) < end_) {1351Char c = getNextChar();1352if (c == '*' && *current_ == '/')1353break;1354if (c == '\n')1355*containsNewLineResult = true;1356}13571358return getNextChar() == '/';1359}13601361bool OurReader::readCppStyleComment() {1362while (current_ != end_) {1363Char c = getNextChar();1364if (c == '\n')1365break;1366if (c == '\r') {1367// Consume DOS EOL. It will be normalized in addComment.1368if (current_ != end_ && *current_ == '\n')1369getNextChar();1370// Break on Moc OS 9 EOL.1371break;1372}1373}1374return true;1375}13761377bool OurReader::readNumber(bool checkInf) {1378Location p = current_;1379if (checkInf && p != end_ && *p == 'I') {1380current_ = ++p;1381return false;1382}1383char c = '0'; // stopgap for already consumed character1384// integral part1385while (c >= '0' && c <= '9')1386c = (current_ = p) < end_ ? *p++ : '\0';1387// fractional part1388if (c == '.') {1389c = (current_ = p) < end_ ? *p++ : '\0';1390while (c >= '0' && c <= '9')1391c = (current_ = p) < end_ ? *p++ : '\0';1392}1393// exponential part1394if (c == 'e' || c == 'E') {1395c = (current_ = p) < end_ ? *p++ : '\0';1396if (c == '+' || c == '-')1397c = (current_ = p) < end_ ? *p++ : '\0';1398while (c >= '0' && c <= '9')1399c = (current_ = p) < end_ ? *p++ : '\0';1400}1401return true;1402}1403bool OurReader::readString() {1404Char c = 0;1405while (current_ != end_) {1406c = getNextChar();1407if (c == '\\')1408getNextChar();1409else if (c == '"')1410break;1411}1412return c == '"';1413}14141415bool OurReader::readStringSingleQuote() {1416Char c = 0;1417while (current_ != end_) {1418c = getNextChar();1419if (c == '\\')1420getNextChar();1421else if (c == '\'')1422break;1423}1424return c == '\'';1425}14261427bool OurReader::readObject(Token& token) {1428Token tokenName;1429String name;1430Value init(objectValue);1431currentValue().swapPayload(init);1432currentValue().setOffsetStart(token.start_ - begin_);1433while (readTokenSkippingComments(tokenName)) {1434if (tokenName.type_ == tokenObjectEnd &&1435(name.empty() ||1436features_.allowTrailingCommas_)) // empty object or trailing comma1437return true;1438name.clear();1439if (tokenName.type_ == tokenString) {1440if (!decodeString(tokenName, name))1441return recoverFromError(tokenObjectEnd);1442} else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {1443Value numberName;1444if (!decodeNumber(tokenName, numberName))1445return recoverFromError(tokenObjectEnd);1446name = numberName.asString();1447} else {1448break;1449}1450if (name.length() >= (1U << 30))1451throwRuntimeError("keylength >= 2^30");1452if (features_.rejectDupKeys_ && currentValue().isMember(name)) {1453String msg = "Duplicate key: '" + name + "'";1454return addErrorAndRecover(msg, tokenName, tokenObjectEnd);1455}14561457Token colon;1458if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {1459return addErrorAndRecover("Missing ':' after object member name", colon,1460tokenObjectEnd);1461}1462Value& value = currentValue()[name];1463nodes_.push(&value);1464bool ok = readValue();1465nodes_.pop();1466if (!ok) // error already set1467return recoverFromError(tokenObjectEnd);14681469Token comma;1470if (!readTokenSkippingComments(comma) ||1471(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {1472return addErrorAndRecover("Missing ',' or '}' in object declaration",1473comma, tokenObjectEnd);1474}1475if (comma.type_ == tokenObjectEnd)1476return true;1477}1478return addErrorAndRecover("Missing '}' or object member name", tokenName,1479tokenObjectEnd);1480}14811482bool OurReader::readArray(Token& token) {1483Value init(arrayValue);1484currentValue().swapPayload(init);1485currentValue().setOffsetStart(token.start_ - begin_);1486int index = 0;1487for (;;) {1488skipSpaces();1489if (current_ != end_ && *current_ == ']' &&1490(index == 0 ||1491(features_.allowTrailingCommas_ &&1492!features_.allowDroppedNullPlaceholders_))) // empty array or trailing1493// comma1494{1495Token endArray;1496readToken(endArray);1497return true;1498}1499Value& value = currentValue()[index++];1500nodes_.push(&value);1501bool ok = readValue();1502nodes_.pop();1503if (!ok) // error already set1504return recoverFromError(tokenArrayEnd);15051506Token currentToken;1507// Accept Comment after last item in the array.1508ok = readTokenSkippingComments(currentToken);1509bool badTokenType = (currentToken.type_ != tokenArraySeparator &&1510currentToken.type_ != tokenArrayEnd);1511if (!ok || badTokenType) {1512return addErrorAndRecover("Missing ',' or ']' in array declaration",1513currentToken, tokenArrayEnd);1514}1515if (currentToken.type_ == tokenArrayEnd)1516break;1517}1518return true;1519}15201521bool OurReader::decodeNumber(Token& token) {1522Value decoded;1523if (!decodeNumber(token, decoded))1524return false;1525currentValue().swapPayload(decoded);1526currentValue().setOffsetStart(token.start_ - begin_);1527currentValue().setOffsetLimit(token.end_ - begin_);1528return true;1529}15301531bool OurReader::decodeNumber(Token& token, Value& decoded) {1532// Attempts to parse the number as an integer. If the number is1533// larger than the maximum supported value of an integer then1534// we decode the number as a double.1535Location current = token.start_;1536const bool isNegative = *current == '-';1537if (isNegative) {1538++current;1539}15401541// We assume we can represent the largest and smallest integer types as1542// unsigned integers with separate sign. This is only true if they can fit1543// into an unsigned integer.1544static_assert(Value::maxLargestInt <= Value::maxLargestUInt,1545"Int must be smaller than UInt");15461547// We need to convert minLargestInt into a positive number. The easiest way1548// to do this conversion is to assume our "threshold" value of minLargestInt1549// divided by 10 can fit in maxLargestInt when absolute valued. This should1550// be a safe assumption.1551static_assert(Value::minLargestInt <= -Value::maxLargestInt,1552"The absolute value of minLargestInt must be greater than or "1553"equal to maxLargestInt");1554static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,1555"The absolute value of minLargestInt must be only 1 magnitude "1556"larger than maxLargest Int");15571558static constexpr Value::LargestUInt positive_threshold =1559Value::maxLargestUInt / 10;1560static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;15611562// For the negative values, we have to be more careful. Since typically1563// -Value::minLargestInt will cause an overflow, we first divide by 10 and1564// then take the inverse. This assumes that minLargestInt is only a single1565// power of 10 different in magnitude, which we check above. For the last1566// digit, we take the modulus before negating for the same reason.1567static constexpr auto negative_threshold =1568Value::LargestUInt(-(Value::minLargestInt / 10));1569static constexpr auto negative_last_digit =1570Value::UInt(-(Value::minLargestInt % 10));15711572const Value::LargestUInt threshold =1573isNegative ? negative_threshold : positive_threshold;1574const Value::UInt max_last_digit =1575isNegative ? negative_last_digit : positive_last_digit;15761577Value::LargestUInt value = 0;1578while (current < token.end_) {1579Char c = *current++;1580if (c < '0' || c > '9')1581return decodeDouble(token, decoded);15821583const auto digit(static_cast<Value::UInt>(c - '0'));1584if (value >= threshold) {1585// We've hit or exceeded the max value divided by 10 (rounded down). If1586// a) we've only just touched the limit, meaning value == threshold,1587// b) this is the last digit, or1588// c) it's small enough to fit in that rounding delta, we're okay.1589// Otherwise treat this number as a double to avoid overflow.1590if (value > threshold || current != token.end_ ||1591digit > max_last_digit) {1592return decodeDouble(token, decoded);1593}1594}1595value = value * 10 + digit;1596}15971598if (isNegative) {1599// We use the same magnitude assumption here, just in case.1600const auto last_digit = static_cast<Value::UInt>(value % 10);1601decoded = -Value::LargestInt(value / 10) * 10 - last_digit;1602} else if (value <= Value::LargestUInt(Value::maxLargestInt)) {1603decoded = Value::LargestInt(value);1604} else {1605decoded = value;1606}16071608return true;1609}16101611bool OurReader::decodeDouble(Token& token) {1612Value decoded;1613if (!decodeDouble(token, decoded))1614return false;1615currentValue().swapPayload(decoded);1616currentValue().setOffsetStart(token.start_ - begin_);1617currentValue().setOffsetLimit(token.end_ - begin_);1618return true;1619}16201621bool OurReader::decodeDouble(Token& token, Value& decoded) {1622double value = 0;1623IStringStream is(String(token.start_, token.end_));1624if (!(is >> value)) {1625if (value == std::numeric_limits<double>::max())1626value = std::numeric_limits<double>::infinity();1627else if (value == std::numeric_limits<double>::lowest())1628value = -std::numeric_limits<double>::infinity();1629else if (!std::isinf(value))1630return addError(1631"'" + String(token.start_, token.end_) + "' is not a number.", token);1632}1633decoded = value;1634return true;1635}16361637bool OurReader::decodeString(Token& token) {1638String decoded_string;1639if (!decodeString(token, decoded_string))1640return false;1641Value decoded(decoded_string);1642currentValue().swapPayload(decoded);1643currentValue().setOffsetStart(token.start_ - begin_);1644currentValue().setOffsetLimit(token.end_ - begin_);1645return true;1646}16471648bool OurReader::decodeString(Token& token, String& decoded) {1649decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));1650Location current = token.start_ + 1; // skip '"'1651Location end = token.end_ - 1; // do not include '"'1652while (current != end) {1653Char c = *current++;1654if (c == '"')1655break;1656if (c == '\\') {1657if (current == end)1658return addError("Empty escape sequence in string", token, current);1659Char escape = *current++;1660switch (escape) {1661case '"':1662decoded += '"';1663break;1664case '/':1665decoded += '/';1666break;1667case '\\':1668decoded += '\\';1669break;1670case 'b':1671decoded += '\b';1672break;1673case 'f':1674decoded += '\f';1675break;1676case 'n':1677decoded += '\n';1678break;1679case 'r':1680decoded += '\r';1681break;1682case 't':1683decoded += '\t';1684break;1685case 'u': {1686unsigned int unicode;1687if (!decodeUnicodeCodePoint(token, current, end, unicode))1688return false;1689decoded += codePointToUTF8(unicode);1690} break;1691default:1692return addError("Bad escape sequence in string", token, current);1693}1694} else {1695decoded += c;1696}1697}1698return true;1699}17001701bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,1702Location end, unsigned int& unicode) {17031704unicode = 0; // Convince clang-analyzer that this is initialized before use.1705if (!decodeUnicodeEscapeSequence(token, current, end, unicode))1706return false;1707if (unicode >= 0xD800 && unicode <= 0xDBFF) {1708// surrogate pairs1709if (end - current < 6)1710return addError(1711"additional six characters expected to parse unicode surrogate pair.",1712token, current);1713if (*(current++) == '\\' && *(current++) == 'u') {1714unsigned int surrogatePair;1715if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {1716unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);1717} else1718return false;1719} else1720return addError("expecting another \\u token to begin the second half of "1721"a unicode surrogate pair",1722token, current);1723}1724return true;1725}17261727bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,1728Location end,1729unsigned int& ret_unicode) {1730if (end - current < 4)1731return addError(1732"Bad unicode escape sequence in string: four digits expected.", token,1733current);1734int unicode = 0;1735for (int index = 0; index < 4; ++index) {1736Char c = *current++;1737unicode *= 16;1738if (c >= '0' && c <= '9')1739unicode += c - '0';1740else if (c >= 'a' && c <= 'f')1741unicode += c - 'a' + 10;1742else if (c >= 'A' && c <= 'F')1743unicode += c - 'A' + 10;1744else1745return addError(1746"Bad unicode escape sequence in string: hexadecimal digit expected.",1747token, current);1748}1749ret_unicode = static_cast<unsigned int>(unicode);1750return true;1751}17521753bool OurReader::addError(const String& message, Token& token, Location extra) {1754ErrorInfo info;1755info.token_ = token;1756info.message_ = message;1757info.extra_ = extra;1758errors_.push_back(info);1759return false;1760}17611762bool OurReader::recoverFromError(TokenType skipUntilToken) {1763size_t errorCount = errors_.size();1764Token skip;1765for (;;) {1766if (!readToken(skip))1767errors_.resize(errorCount); // discard errors caused by recovery1768if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)1769break;1770}1771errors_.resize(errorCount);1772return false;1773}17741775bool OurReader::addErrorAndRecover(const String& message, Token& token,1776TokenType skipUntilToken) {1777addError(message, token);1778return recoverFromError(skipUntilToken);1779}17801781Value& OurReader::currentValue() { return *(nodes_.top()); }17821783OurReader::Char OurReader::getNextChar() {1784if (current_ == end_)1785return 0;1786return *current_++;1787}17881789void OurReader::getLocationLineAndColumn(Location location, int& line,1790int& column) const {1791Location current = begin_;1792Location lastLineStart = current;1793line = 0;1794while (current < location && current != end_) {1795Char c = *current++;1796if (c == '\r') {1797if (current != end_ && *current == '\n')1798++current;1799lastLineStart = current;1800++line;1801} else if (c == '\n') {1802lastLineStart = current;1803++line;1804}1805}1806// column & line start at 11807column = int(location - lastLineStart) + 1;1808++line;1809}18101811String OurReader::getLocationLineAndColumn(Location location) const {1812int line, column;1813getLocationLineAndColumn(location, line, column);1814char buffer[18 + 16 + 16 + 1];1815jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);1816return buffer;1817}18181819String OurReader::getFormattedErrorMessages() const {1820String formattedMessage;1821for (const auto& error : errors_) {1822formattedMessage +=1823"* " + getLocationLineAndColumn(error.token_.start_) + "\n";1824formattedMessage += " " + error.message_ + "\n";1825if (error.extra_)1826formattedMessage +=1827"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";1828}1829return formattedMessage;1830}18311832std::vector<CharReader::StructuredError>1833OurReader::getStructuredErrors() const {1834std::vector<CharReader::StructuredError> allErrors;1835for (const auto& error : errors_) {1836CharReader::StructuredError structured;1837structured.offset_start = error.token_.start_ - begin_;1838structured.offset_limit = error.token_.end_ - begin_;1839structured.message = error.message_;1840allErrors.push_back(structured);1841}1842return allErrors;1843}18441845class OurCharReader : public CharReader {18461847public:1848OurCharReader(bool collectComments, OurFeatures const& features)1849: CharReader(1850std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}18511852protected:1853class OurImpl : public Impl {1854public:1855OurImpl(bool collectComments, OurFeatures const& features)1856: collectComments_(collectComments), reader_(features) {}18571858bool parse(char const* beginDoc, char const* endDoc, Value* root,1859String* errs) override {1860bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);1861if (errs) {1862*errs = reader_.getFormattedErrorMessages();1863}1864return ok;1865}18661867std::vector<CharReader::StructuredError>1868getStructuredErrors() const override {1869return reader_.getStructuredErrors();1870}18711872private:1873bool const collectComments_;1874OurReader reader_;1875};1876};18771878CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }1879CharReaderBuilder::~CharReaderBuilder() = default;1880CharReader* CharReaderBuilder::newCharReader() const {1881bool collectComments = settings_["collectComments"].asBool();1882OurFeatures features = OurFeatures::all();1883features.allowComments_ = settings_["allowComments"].asBool();1884features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();1885features.strictRoot_ = settings_["strictRoot"].asBool();1886features.allowDroppedNullPlaceholders_ =1887settings_["allowDroppedNullPlaceholders"].asBool();1888features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();1889features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();18901891// Stack limit is always a size_t, so we get this as an unsigned int1892// regardless of it we have 64-bit integer support enabled.1893features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());1894features.failIfExtra_ = settings_["failIfExtra"].asBool();1895features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();1896features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();1897features.skipBom_ = settings_["skipBom"].asBool();1898return new OurCharReader(collectComments, features);1899}19001901bool CharReaderBuilder::validate(Json::Value* invalid) const {1902static const auto& valid_keys = *new std::set<String>{1903"collectComments",1904"allowComments",1905"allowTrailingCommas",1906"strictRoot",1907"allowDroppedNullPlaceholders",1908"allowNumericKeys",1909"allowSingleQuotes",1910"stackLimit",1911"failIfExtra",1912"rejectDupKeys",1913"allowSpecialFloats",1914"skipBom",1915};1916for (auto si = settings_.begin(); si != settings_.end(); ++si) {1917auto key = si.name();1918if (valid_keys.count(key))1919continue;1920if (invalid)1921(*invalid)[key] = *si;1922else1923return false;1924}1925return invalid ? invalid->empty() : true;1926}19271928Value& CharReaderBuilder::operator[](const String& key) {1929return settings_[key];1930}1931// static1932void CharReaderBuilder::strictMode(Json::Value* settings) {1933//! [CharReaderBuilderStrictMode]1934(*settings)["allowComments"] = false;1935(*settings)["allowTrailingCommas"] = false;1936(*settings)["strictRoot"] = true;1937(*settings)["allowDroppedNullPlaceholders"] = false;1938(*settings)["allowNumericKeys"] = false;1939(*settings)["allowSingleQuotes"] = false;1940(*settings)["stackLimit"] = 1000;1941(*settings)["failIfExtra"] = true;1942(*settings)["rejectDupKeys"] = true;1943(*settings)["allowSpecialFloats"] = false;1944(*settings)["skipBom"] = true;1945//! [CharReaderBuilderStrictMode]1946}1947// static1948void CharReaderBuilder::setDefaults(Json::Value* settings) {1949//! [CharReaderBuilderDefaults]1950(*settings)["collectComments"] = true;1951(*settings)["allowComments"] = true;1952(*settings)["allowTrailingCommas"] = true;1953(*settings)["strictRoot"] = false;1954(*settings)["allowDroppedNullPlaceholders"] = false;1955(*settings)["allowNumericKeys"] = false;1956(*settings)["allowSingleQuotes"] = false;1957(*settings)["stackLimit"] = 1000;1958(*settings)["failIfExtra"] = false;1959(*settings)["rejectDupKeys"] = false;1960(*settings)["allowSpecialFloats"] = false;1961(*settings)["skipBom"] = true;1962//! [CharReaderBuilderDefaults]1963}1964// static1965void CharReaderBuilder::ecma404Mode(Json::Value* settings) {1966//! [CharReaderBuilderECMA404Mode]1967(*settings)["allowComments"] = false;1968(*settings)["allowTrailingCommas"] = false;1969(*settings)["strictRoot"] = false;1970(*settings)["allowDroppedNullPlaceholders"] = false;1971(*settings)["allowNumericKeys"] = false;1972(*settings)["allowSingleQuotes"] = false;1973(*settings)["stackLimit"] = 1000;1974(*settings)["failIfExtra"] = true;1975(*settings)["rejectDupKeys"] = false;1976(*settings)["allowSpecialFloats"] = false;1977(*settings)["skipBom"] = false;1978//! [CharReaderBuilderECMA404Mode]1979}19801981std::vector<CharReader::StructuredError>1982CharReader::getStructuredErrors() const {1983return _impl->getStructuredErrors();1984}19851986bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,1987String* errs) {1988return _impl->parse(beginDoc, endDoc, root, errs);1989}19901991//////////////////////////////////1992// global functions19931994bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,1995String* errs) {1996OStringStream ssin;1997ssin << sin.rdbuf();1998String doc = std::move(ssin).str();1999char const* begin = doc.data();2000char const* end = begin + doc.size();2001// Note that we do not actually need a null-terminator.2002CharReaderPtr const reader(fact.newCharReader());2003return reader->parse(begin, end, root, errs);2004}20052006IStream& operator>>(IStream& sin, Value& root) {2007CharReaderBuilder b;2008String errs;2009bool ok = parseFromStream(b, sin, &root, &errs);2010if (!ok) {2011throwRuntimeError(errs);2012}2013return sin;2014}20152016} // namespace Json201720182019