JsonCpp 1.10.0
JSON data format manipulation library
Loading...
Searching...
No Matches
json_reader.cpp
Go to the documentation of this file.
1// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
2// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
3// Distributed under MIT license, or public domain if desired and
4// recognized in your jurisdiction.
5// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
6
7#if !defined(JSON_IS_AMALGAMATION)
8#include "json_tool.h"
9#include <json/assertions.h>
10#include <json/reader.h>
11#include <json/value.h>
12#endif // if !defined(JSON_IS_AMALGAMATION)
13#include <algorithm>
14#include <cassert>
15#include <cmath>
16#include <cstring>
17#include <iostream>
18#include <istream>
19#include <iterator>
20#include <limits>
21#include <memory>
22#include <set>
23#include <sstream>
24#include <utility>
25
26#include <cstdio>
27
28#if defined(_MSC_VER)
29#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
30#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
31#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
32#endif //_MSC_VER
33
34#if defined(_MSC_VER)
35// Disable warning about strdup being deprecated.
36#pragma warning(disable : 4996)
37#endif
38
39// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
40// time to change the stack limit
41#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
42#define JSONCPP_DEPRECATED_STACK_LIMIT 256
43#endif
44
45static size_t const stackLimit_g =
46 JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
47
48namespace Json {
49
50using CharReaderPtr = std::unique_ptr<CharReader>;
51
52// Implementation of class Features
53// ////////////////////////////////
54
55Features::Features() = default;
56
57Features Features::all() { return {}; }
58
60 Features features;
61 features.allowComments_ = false;
62 features.strictRoot_ = true;
63 features.allowDroppedNullPlaceholders_ = false;
64 features.allowNumericKeys_ = false;
65 return features;
66}
67
68// Implementation of class Reader
69// ////////////////////////////////
70
71bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
72 return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
73}
74
75// Class Reader
76// //////////////////////////////////////////////////////////////////
77
78Reader::Reader() : features_(Features::all()) {}
79
80Reader::Reader(const Features& features) : features_(features) {}
81
82bool Reader::parse(const std::string& document, Value& root,
83 bool collectComments) {
84 document_.assign(document.begin(), document.end());
85 const char* begin = document_.c_str();
86 const char* end = begin + document_.length();
87 return parse(begin, end, root, collectComments);
88}
89
90bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
91 document_.assign(std::istreambuf_iterator<char>(is),
92 std::istreambuf_iterator<char>());
93 return parse(document_.data(), document_.data() + document_.size(), root,
94 collectComments);
95}
96
97bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
98 bool collectComments) {
99 if (!features_.allowComments_) {
100 collectComments = false;
101 }
102
103 begin_ = beginDoc;
104 end_ = endDoc;
105 collectComments_ = collectComments;
106 current_ = begin_;
107 lastValueEnd_ = nullptr;
108 lastValue_ = nullptr;
109 commentsBefore_.clear();
110 errors_.clear();
111 while (!nodes_.empty())
112 nodes_.pop();
113 nodes_.push(&root);
114
115 bool successful = readValue();
116 Token token;
117 readTokenSkippingComments(token);
118 if (collectComments_ && !commentsBefore_.empty())
119 root.setComment(commentsBefore_, commentAfter);
120 if (features_.strictRoot_) {
121 if (!root.isArray() && !root.isObject()) {
122 // Set error location to start of doc, ideally should be first token found
123 // in doc
124 token.type_ = tokenError;
125 token.start_ = beginDoc;
126 token.end_ = endDoc;
127 addError(
128 "A valid JSON document must be either an array or an object value.",
129 token);
130 return false;
131 }
132 }
133 return successful;
134}
135
136bool Reader::readValue() {
137 // readValue() may call itself only if it calls readObject() or ReadArray().
138 // These methods execute nodes_.push() just before and nodes_.pop)() just
139 // after calling readValue(). parse() executes one nodes_.push(), so > instead
140 // of >=.
141 if (nodes_.size() > stackLimit_g)
142#if JSON_USE_EXCEPTION
143 throwRuntimeError("Exceeded stackLimit in readValue().");
144#else
145 // throwRuntimeError aborts. Don't abort here.
146 return false;
147#endif
148
149 Token token;
150 readTokenSkippingComments(token);
151 bool successful = true;
152
153 if (collectComments_ && !commentsBefore_.empty()) {
154 currentValue().setComment(commentsBefore_, commentBefore);
155 commentsBefore_.clear();
156 }
157
158 switch (token.type_) {
159 case tokenObjectBegin:
160 successful = readObject(token);
161 currentValue().setOffsetLimit(current_ - begin_);
162 break;
163 case tokenArrayBegin:
164 successful = readArray(token);
165 currentValue().setOffsetLimit(current_ - begin_);
166 break;
167 case tokenNumber:
168 successful = decodeNumber(token);
169 break;
170 case tokenString:
171 successful = decodeString(token);
172 break;
173 case tokenTrue: {
174 Value v(true);
175 currentValue().swapPayload(v);
176 currentValue().setOffsetStart(token.start_ - begin_);
177 currentValue().setOffsetLimit(token.end_ - begin_);
178 } break;
179 case tokenFalse: {
180 Value v(false);
181 currentValue().swapPayload(v);
182 currentValue().setOffsetStart(token.start_ - begin_);
183 currentValue().setOffsetLimit(token.end_ - begin_);
184 } break;
185 case tokenNull: {
186 Value v;
187 currentValue().swapPayload(v);
188 currentValue().setOffsetStart(token.start_ - begin_);
189 currentValue().setOffsetLimit(token.end_ - begin_);
190 } break;
191 case tokenArraySeparator:
192 case tokenObjectEnd:
193 case tokenArrayEnd:
194 if (features_.allowDroppedNullPlaceholders_) {
195 // "Un-read" the current token and mark the current value as a null
196 // token.
197 current_--;
198 Value v;
199 currentValue().swapPayload(v);
200 currentValue().setOffsetStart(current_ - begin_ - 1);
201 currentValue().setOffsetLimit(current_ - begin_);
202 break;
203 } // Else, fall through...
204 default:
205 currentValue().setOffsetStart(token.start_ - begin_);
206 currentValue().setOffsetLimit(token.end_ - begin_);
207 return addError("Syntax error: value, object or array expected.", token);
208 }
209
210 if (collectComments_) {
211 lastValueEnd_ = current_;
212 lastValue_ = &currentValue();
213 }
214
215 return successful;
216}
217
218bool Reader::readTokenSkippingComments(Token& token) {
219 bool success = readToken(token);
220 if (features_.allowComments_) {
221 while (success && token.type_ == tokenComment) {
222 success = readToken(token);
223 }
224 }
225 return success;
226}
227
228bool Reader::readToken(Token& token) {
229 skipSpaces();
230 token.start_ = current_;
231 Char c = getNextChar();
232 bool ok = true;
233 switch (c) {
234 case '{':
235 token.type_ = tokenObjectBegin;
236 break;
237 case '}':
238 token.type_ = tokenObjectEnd;
239 break;
240 case '[':
241 token.type_ = tokenArrayBegin;
242 break;
243 case ']':
244 token.type_ = tokenArrayEnd;
245 break;
246 case '"':
247 token.type_ = tokenString;
248 ok = readString();
249 break;
250 case '/':
251 token.type_ = tokenComment;
252 ok = readComment();
253 break;
254 case '0':
255 case '1':
256 case '2':
257 case '3':
258 case '4':
259 case '5':
260 case '6':
261 case '7':
262 case '8':
263 case '9':
264 case '-':
265 token.type_ = tokenNumber;
266 readNumber();
267 break;
268 case 't':
269 token.type_ = tokenTrue;
270 ok = match("rue", 3);
271 break;
272 case 'f':
273 token.type_ = tokenFalse;
274 ok = match("alse", 4);
275 break;
276 case 'n':
277 token.type_ = tokenNull;
278 ok = match("ull", 3);
279 break;
280 case ',':
281 token.type_ = tokenArraySeparator;
282 break;
283 case ':':
284 token.type_ = tokenMemberSeparator;
285 break;
286 case 0:
287 token.type_ = tokenEndOfStream;
288 break;
289 default:
290 ok = false;
291 break;
292 }
293 if (!ok)
294 token.type_ = tokenError;
295 token.end_ = current_;
296 return ok;
297}
298
299void Reader::skipSpaces() {
300 while (current_ != end_) {
301 Char c = *current_;
302 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
303 ++current_;
304 else
305 break;
306 }
307}
308
309bool Reader::match(const Char* pattern, int patternLength) {
310 if (end_ - current_ < patternLength)
311 return false;
312 int index = patternLength;
313 while (index--)
314 if (current_[index] != pattern[index])
315 return false;
316 current_ += patternLength;
317 return true;
318}
319
320bool Reader::readComment() {
321 Location commentBegin = current_ - 1;
322 Char c = getNextChar();
323 bool successful = false;
324 if (c == '*')
325 successful = readCStyleComment();
326 else if (c == '/')
327 successful = readCppStyleComment();
328 if (!successful)
329 return false;
330
331 if (collectComments_) {
333 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
334 if (c != '*' || !containsNewLine(commentBegin, current_))
335 placement = commentAfterOnSameLine;
336 }
337
338 addComment(commentBegin, current_, placement);
339 }
340 return true;
341}
342
343String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
344 String normalized;
345 normalized.reserve(static_cast<size_t>(end - begin));
346 Reader::Location current = begin;
347 while (current != end) {
348 char c = *current++;
349 if (c == '\r') {
350 if (current != end && *current == '\n')
351 // convert dos EOL
352 ++current;
353 // convert Mac EOL
354 normalized += '\n';
355 } else {
356 normalized += c;
357 }
358 }
359 return normalized;
360}
361
362void Reader::addComment(Location begin, Location end,
363 CommentPlacement placement) {
364 assert(collectComments_);
365 const String& normalized = normalizeEOL(begin, end);
366 if (placement == commentAfterOnSameLine) {
367 assert(lastValue_ != nullptr);
368 lastValue_->setComment(normalized, placement);
369 } else {
370 commentsBefore_ += normalized;
371 }
372}
373
374bool Reader::readCStyleComment() {
375 while ((current_ + 1) < end_) {
376 Char c = getNextChar();
377 if (c == '*' && *current_ == '/')
378 break;
379 }
380 return getNextChar() == '/';
381}
382
383bool Reader::readCppStyleComment() {
384 while (current_ != end_) {
385 Char c = getNextChar();
386 if (c == '\n')
387 break;
388 if (c == '\r') {
389 // Consume DOS EOL. It will be normalized in addComment.
390 if (current_ != end_ && *current_ == '\n')
391 getNextChar();
392 // Break on Moc OS 9 EOL.
393 break;
394 }
395 }
396 return true;
397}
398
399void Reader::readNumber() {
400 Location p = current_;
401 char c = '0'; // stopgap for already consumed character
402 // integral part
403 while (c >= '0' && c <= '9')
404 c = (current_ = p) < end_ ? *p++ : '\0';
405 // fractional part
406 if (c == '.') {
407 c = (current_ = p) < end_ ? *p++ : '\0';
408 while (c >= '0' && c <= '9')
409 c = (current_ = p) < end_ ? *p++ : '\0';
410 }
411 // exponential part
412 if (c == 'e' || c == 'E') {
413 c = (current_ = p) < end_ ? *p++ : '\0';
414 if (c == '+' || c == '-')
415 c = (current_ = p) < end_ ? *p++ : '\0';
416 while (c >= '0' && c <= '9')
417 c = (current_ = p) < end_ ? *p++ : '\0';
418 }
419}
420
421bool Reader::readString() {
422 Char c = '\0';
423 while (current_ != end_) {
424 c = getNextChar();
425 if (c == '\\')
426 getNextChar();
427 else if (c == '"')
428 break;
429 }
430 return c == '"';
431}
432
433bool Reader::readObject(Token& token) {
434 Token tokenName;
435 String name;
436 Value init(objectValue);
437 currentValue().swapPayload(init);
438 currentValue().setOffsetStart(token.start_ - begin_);
439 while (readTokenSkippingComments(tokenName)) {
440 if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
441 return true;
442 name.clear();
443 if (tokenName.type_ == tokenString) {
444 if (!decodeString(tokenName, name))
445 return recoverFromError(tokenObjectEnd);
446 } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
447 Value numberName;
448 if (!decodeNumber(tokenName, numberName))
449 return recoverFromError(tokenObjectEnd);
450 name = numberName.asString();
451 } else {
452 break;
453 }
454
455 Token colon;
456 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
457 return addErrorAndRecover("Missing ':' after object member name", colon,
458 tokenObjectEnd);
459 }
460 Value& value = currentValue()[name];
461 nodes_.push(&value);
462 bool ok = readValue();
463 nodes_.pop();
464 if (!ok) // error already set
465 return recoverFromError(tokenObjectEnd);
466
467 Token comma;
468 if (!readTokenSkippingComments(comma) ||
469 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
470 return addErrorAndRecover("Missing ',' or '}' in object declaration",
471 comma, tokenObjectEnd);
472 }
473 if (comma.type_ == tokenObjectEnd)
474 return true;
475 }
476 return addErrorAndRecover("Missing '}' or object member name", tokenName,
477 tokenObjectEnd);
478}
479
480bool Reader::readArray(Token& token) {
481 Value init(arrayValue);
482 currentValue().swapPayload(init);
483 currentValue().setOffsetStart(token.start_ - begin_);
484 skipSpaces();
485 if (current_ != end_ && *current_ == ']') // empty array
486 {
487 Token endArray;
488 readToken(endArray);
489 return true;
490 }
491 int index = 0;
492 for (;;) {
493 Value& value = currentValue()[index++];
494 nodes_.push(&value);
495 bool ok = readValue();
496 nodes_.pop();
497 if (!ok) // error already set
498 return recoverFromError(tokenArrayEnd);
499
500 Token currentToken;
501 // Accept Comment after last item in the array.
502 ok = readTokenSkippingComments(currentToken);
503 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
504 currentToken.type_ != tokenArrayEnd);
505 if (!ok || badTokenType) {
506 return addErrorAndRecover("Missing ',' or ']' in array declaration",
507 currentToken, tokenArrayEnd);
508 }
509 if (currentToken.type_ == tokenArrayEnd)
510 break;
511 }
512 return true;
513}
514
515bool Reader::decodeNumber(Token& token) {
516 Value decoded;
517 if (!decodeNumber(token, decoded))
518 return false;
519 currentValue().swapPayload(decoded);
520 currentValue().setOffsetStart(token.start_ - begin_);
521 currentValue().setOffsetLimit(token.end_ - begin_);
522 return true;
523}
524
525bool Reader::decodeNumber(Token& token, Value& decoded) {
526 // Attempts to parse the number as an integer. If the number is
527 // larger than the maximum supported value of an integer then
528 // we decode the number as a double.
529 Location current = token.start_;
530 bool isNegative = *current == '-';
531 if (isNegative)
532 ++current;
533 // TODO: Help the compiler do the div and mod at compile time or get rid of
534 // them.
535 Value::LargestUInt maxIntegerValue =
538 Value::LargestUInt threshold = maxIntegerValue / 10;
539 Value::LargestUInt value = 0;
540 while (current < token.end_) {
541 Char c = *current++;
542 if (c < '0' || c > '9')
543 return decodeDouble(token, decoded);
544 auto digit(static_cast<Value::UInt>(c - '0'));
545 if (value >= threshold) {
546 // We've hit or exceeded the max value divided by 10 (rounded down). If
547 // a) we've only just touched the limit, b) this is the last digit, and
548 // c) it's small enough to fit in that rounding delta, we're okay.
549 // Otherwise treat this number as a double to avoid overflow.
550 if (value > threshold || current != token.end_ ||
551 digit > maxIntegerValue % 10) {
552 return decodeDouble(token, decoded);
553 }
554 }
555 value = value * 10 + digit;
556 }
557 if (isNegative && value == maxIntegerValue)
558 decoded = Value::minLargestInt;
559 else if (isNegative)
560 decoded = -Value::LargestInt(value);
561 else if (value <= Value::LargestUInt(Value::maxInt))
562 decoded = Value::LargestInt(value);
563 else
564 decoded = value;
565 return true;
566}
567
568bool Reader::decodeDouble(Token& token) {
569 Value decoded;
570 if (!decodeDouble(token, decoded))
571 return false;
572 currentValue().swapPayload(decoded);
573 currentValue().setOffsetStart(token.start_ - begin_);
574 currentValue().setOffsetLimit(token.end_ - begin_);
575 return true;
576}
577
578bool Reader::decodeDouble(Token& token, Value& decoded) {
579 double value = 0;
580 IStringStream is(String(token.start_, token.end_));
581 is.imbue(std::locale::classic());
582 if (!(is >> value)) {
583 if (value == std::numeric_limits<double>::max())
584 value = std::numeric_limits<double>::infinity();
585 else if (value == std::numeric_limits<double>::lowest())
586 value = -std::numeric_limits<double>::infinity();
587 // operator>> sets failbit for a subnormal result (underflow) even though
588 // it produced the correctly-rounded value, which made such numbers fail to
589 // parse back after jsoncpp serialized them. Keep a subnormal value instead
590 // of rejecting it. See issue #1427. Other failures -- malformed numbers
591 // like "0e" or "0e+", or non-numbers -- leave the value at zero/non-finite
592 // and are still rejected.
593 else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
594 return addError(
595 "'" + String(token.start_, token.end_) + "' is not a number.", token);
596 }
597 decoded = value;
598 return true;
599}
600
601bool Reader::decodeString(Token& token) {
602 String decoded_string;
603 if (!decodeString(token, decoded_string))
604 return false;
605 Value decoded(decoded_string);
606 currentValue().swapPayload(decoded);
607 currentValue().setOffsetStart(token.start_ - begin_);
608 currentValue().setOffsetLimit(token.end_ - begin_);
609 return true;
610}
611
612bool Reader::decodeString(Token& token, String& decoded) {
613 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
614 Location current = token.start_ + 1; // skip '"'
615 Location end = token.end_ - 1; // do not include '"'
616 while (current != end) {
617 Char c = *current++;
618 if (c == '"')
619 break;
620 if (c == '\\') {
621 if (current == end)
622 return addError("Empty escape sequence in string", token, current);
623 Char escape = *current++;
624 switch (escape) {
625 case '"':
626 decoded += '"';
627 break;
628 case '/':
629 decoded += '/';
630 break;
631 case '\\':
632 decoded += '\\';
633 break;
634 case 'b':
635 decoded += '\b';
636 break;
637 case 'f':
638 decoded += '\f';
639 break;
640 case 'n':
641 decoded += '\n';
642 break;
643 case 'r':
644 decoded += '\r';
645 break;
646 case 't':
647 decoded += '\t';
648 break;
649 case 'u': {
650 unsigned int unicode;
651 if (!decodeUnicodeCodePoint(token, current, end, unicode))
652 return false;
653 decoded += codePointToUTF8(unicode);
654 } break;
655 default:
656 return addError("Bad escape sequence in string", token, current);
657 }
658 } else {
659 if (static_cast<unsigned char>(c) < 0x20)
660 return addError("Control character in string", token, current - 1);
661 decoded += c;
662 }
663 }
664 return true;
665}
666
667bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
668 Location end, unsigned int& unicode) {
669
670 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
671 return false;
672 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
673 // surrogate pairs
674 if (end - current < 6)
675 return addError(
676 "additional six characters expected to parse unicode surrogate pair.",
677 token, current);
678 if (*(current++) == '\\' && *(current++) == 'u') {
679 unsigned int surrogatePair;
680 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
681 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
682 } else
683 return false;
684 } else
685 return addError("expecting another \\u token to begin the second half of "
686 "a unicode surrogate pair",
687 token, current);
688 }
689 return true;
690}
691
692bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
693 Location end,
694 unsigned int& ret_unicode) {
695 if (end - current < 4)
696 return addError(
697 "Bad unicode escape sequence in string: four digits expected.", token,
698 current);
699 int unicode = 0;
700 for (int index = 0; index < 4; ++index) {
701 Char c = *current++;
702 unicode *= 16;
703 if (c >= '0' && c <= '9')
704 unicode += c - '0';
705 else if (c >= 'a' && c <= 'f')
706 unicode += c - 'a' + 10;
707 else if (c >= 'A' && c <= 'F')
708 unicode += c - 'A' + 10;
709 else
710 return addError(
711 "Bad unicode escape sequence in string: hexadecimal digit expected.",
712 token, current);
713 }
714 ret_unicode = static_cast<unsigned int>(unicode);
715 return true;
716}
717
718bool Reader::addError(const String& message, Token& token, Location extra) {
719 ErrorInfo info;
720 info.token_ = token;
721 info.message_ = message;
722 info.extra_ = extra;
723 errors_.push_back(info);
724 return false;
725}
726
727bool Reader::recoverFromError(TokenType skipUntilToken) {
728 size_t const errorCount = errors_.size();
729 Token skip;
730 for (;;) {
731 if (!readToken(skip))
732 errors_.resize(errorCount); // discard errors caused by recovery
733 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
734 break;
735 }
736 errors_.resize(errorCount);
737 return false;
738}
739
740bool Reader::addErrorAndRecover(const String& message, Token& token,
741 TokenType skipUntilToken) {
742 addError(message, token);
743 return recoverFromError(skipUntilToken);
744}
745
746Value& Reader::currentValue() { return *(nodes_.top()); }
747
748Reader::Char Reader::getNextChar() {
749 if (current_ == end_)
750 return 0;
751 return *current_++;
752}
753
754void Reader::getLocationLineAndColumn(Location location, int& line,
755 int& column) const {
756 Location current = begin_;
757 Location lastLineStart = current;
758 line = 0;
759 while (current < location && current != end_) {
760 Char c = *current++;
761 if (c == '\r') {
762 if (current != end_ && *current == '\n')
763 ++current;
764 lastLineStart = current;
765 ++line;
766 } else if (c == '\n') {
767 lastLineStart = current;
768 ++line;
769 }
770 }
771 // column & line start at 1
772 column = int(location - lastLineStart) + 1;
773 ++line;
774}
775
776String Reader::getLocationLineAndColumn(Location location) const {
777 int line, column;
778 getLocationLineAndColumn(location, line, column);
779 char buffer[18 + 16 + 16 + 1];
780 jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
781 return buffer;
782}
783
784// Deprecated. Preserved for backward compatibility
788
790 String formattedMessage;
791 for (const auto& error : errors_) {
792 formattedMessage +=
793 "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
794 formattedMessage += " " + error.message_ + "\n";
795 if (error.extra_)
796 formattedMessage +=
797 "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
798 }
799 return formattedMessage;
800}
801
802std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
803 std::vector<Reader::StructuredError> allErrors;
804 for (const auto& error : errors_) {
805 Reader::StructuredError structured;
806 structured.offset_start = error.token_.start_ - begin_;
807 structured.offset_limit = error.token_.end_ - begin_;
808 structured.message = error.message_;
809 allErrors.push_back(structured);
810 }
811 return allErrors;
812}
813
814bool Reader::pushError(const Value& value, const String& message) {
815 ptrdiff_t const length = end_ - begin_;
816 if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
817 return false;
818 Token token;
819 token.type_ = tokenError;
820 token.start_ = begin_ + value.getOffsetStart();
821 token.end_ = begin_ + value.getOffsetLimit();
822 ErrorInfo info;
823 info.token_ = token;
824 info.message_ = message;
825 info.extra_ = nullptr;
826 errors_.push_back(info);
827 return true;
828}
829
830bool Reader::pushError(const Value& value, const String& message,
831 const Value& extra) {
832 ptrdiff_t const length = end_ - begin_;
833 if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
834 extra.getOffsetLimit() > length)
835 return false;
836 Token token;
837 token.type_ = tokenError;
838 token.start_ = begin_ + value.getOffsetStart();
839 token.end_ = begin_ + value.getOffsetLimit();
840 ErrorInfo info;
841 info.token_ = token;
842 info.message_ = message;
843 info.extra_ = begin_ + extra.getOffsetStart();
844 errors_.push_back(info);
845 return true;
846}
847
848bool Reader::good() const { return errors_.empty(); }
849
850// Originally copied from the Features class (now deprecated), used internally
851// for features implementation.
852class OurFeatures {
853public:
854 static OurFeatures all();
855 bool allowComments_;
856 bool allowTrailingCommas_;
857 bool strictRoot_;
858 bool allowDroppedNullPlaceholders_;
859 bool allowNumericKeys_;
860 bool allowSingleQuotes_;
861 bool failIfExtra_;
862 bool rejectDupKeys_;
863 bool allowSpecialFloats_;
864 bool skipBom_;
865 size_t stackLimit_;
866}; // OurFeatures
867
868OurFeatures OurFeatures::all() { return {}; }
869
870// Implementation of class Reader
871// ////////////////////////////////
872
873// Originally copied from the Reader class (now deprecated), used internally
874// for implementing JSON reading.
875class OurReader {
876public:
877 using Char = char;
878 using Location = const Char*;
879
880 explicit OurReader(OurFeatures const& features);
881 bool parse(const char* beginDoc, const char* endDoc, Value& root,
882 bool collectComments = true);
883 String getFormattedErrorMessages() const;
884 std::vector<CharReader::StructuredError> getStructuredErrors() const;
885
886private:
887 OurReader(OurReader const&); // no impl
888 void operator=(OurReader const&); // no impl
889
890 enum TokenType {
891 tokenEndOfStream = 0,
892 tokenObjectBegin,
893 tokenObjectEnd,
894 tokenArrayBegin,
895 tokenArrayEnd,
896 tokenString,
897 tokenNumber,
898 tokenTrue,
899 tokenFalse,
900 tokenNull,
901 tokenNaN,
902 tokenPosInf,
903 tokenNegInf,
904 tokenArraySeparator,
905 tokenMemberSeparator,
906 tokenComment,
907 tokenError
908 };
909
910 class Token {
911 public:
912 TokenType type_;
913 Location start_;
914 Location end_;
915 };
916
917 class ErrorInfo {
918 public:
919 Token token_;
920 String message_;
921 Location extra_;
922 };
923
924 using Errors = std::deque<ErrorInfo>;
925
926 bool readToken(Token& token);
927 bool readTokenSkippingComments(Token& token);
928 void skipSpaces();
929 void skipCommentTokens();
930 void skipBom(bool skipBom);
931 bool match(const Char* pattern, int patternLength);
932 bool readComment();
933 bool readCStyleComment(bool* containsNewLineResult);
934 bool readCppStyleComment();
935 bool readString();
936 bool readStringSingleQuote();
937 bool readNumber(bool checkInf);
938 bool readValue();
939 bool readObject(Token& token);
940 bool readArray(Token& token);
941 bool decodeNumber(Token& token);
942 bool decodeNumber(Token& token, Value& decoded);
943 bool decodeString(Token& token);
944 bool decodeString(Token& token, String& decoded);
945 bool decodeDouble(Token& token);
946 bool decodeDouble(Token& token, Value& decoded);
947 bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
948 unsigned int& unicode);
949 bool decodeUnicodeEscapeSequence(Token& token, Location& current,
950 Location end, unsigned int& unicode);
951 bool addError(const String& message, Token& token, Location extra = nullptr);
952 bool recoverFromError(TokenType skipUntilToken);
953 bool addErrorAndRecover(const String& message, Token& token,
954 TokenType skipUntilToken);
955 void skipUntilSpace();
956 Value& currentValue();
957 Char getNextChar();
958 void getLocationLineAndColumn(Location location, int& line,
959 int& column) const;
960 String getLocationLineAndColumn(Location location) const;
961 void addComment(Location begin, Location end, CommentPlacement placement);
962
963 static String normalizeEOL(Location begin, Location end);
964 static bool containsNewLine(Location begin, Location end);
965
966 using Nodes = std::stack<Value*>;
967
968 Nodes nodes_{};
969 Errors errors_{};
970 String document_{};
971 Location begin_ = nullptr;
972 Location end_ = nullptr;
973 Location current_ = nullptr;
974 Location lastValueEnd_ = nullptr;
975 Value* lastValue_ = nullptr;
976 bool lastValueHasAComment_ = false;
977 String commentsBefore_{};
978
979 OurFeatures const features_;
980 bool collectComments_ = false;
981}; // OurReader
982
983// complete copy of Read impl, for OurReader
984
985// Test-only instrumentation: total bytes examined by
986// OurReader::containsNewLine, so unit tests can assert that comment handling
987// stays linear in the input rather than quadratic in the comment count (see
988// CharReaderTest/parseCommentsAfterValueScansLinearly). thread_local so it
989// never races during concurrent parsing; the increment is negligible and only
990// runs while parsing comments. Not part of the supported public API.
992 static thread_local size_t count = 0;
993 return count;
994}
995
996bool OurReader::containsNewLine(OurReader::Location begin,
997 OurReader::Location end) {
998 newlineScanByteCountForTesting() += static_cast<size_t>(end - begin);
999 return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
1000}
1001
1002OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1003
1004bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
1005 bool collectComments) {
1006 if (!features_.allowComments_) {
1007 collectComments = false;
1008 }
1009
1010 begin_ = beginDoc;
1011 end_ = endDoc;
1012 collectComments_ = collectComments;
1013 current_ = begin_;
1014 lastValueEnd_ = nullptr;
1015 lastValue_ = nullptr;
1016 commentsBefore_.clear();
1017 errors_.clear();
1018 while (!nodes_.empty())
1019 nodes_.pop();
1020 nodes_.push(&root);
1021
1022 // skip byte order mark if it exists at the beginning of the UTF-8 text.
1023 skipBom(features_.skipBom_);
1024 bool successful = readValue();
1025 nodes_.pop();
1026 Token token;
1027 readTokenSkippingComments(token);
1028 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1029 addError("Extra non-whitespace after JSON value.", token);
1030 return false;
1031 }
1032 if (collectComments_ && !commentsBefore_.empty())
1033 root.setComment(commentsBefore_, commentAfter);
1034 if (features_.strictRoot_) {
1035 if (!root.isArray() && !root.isObject()) {
1036 // Set error location to start of doc, ideally should be first token found
1037 // in doc
1038 token.type_ = tokenError;
1039 token.start_ = beginDoc;
1040 token.end_ = endDoc;
1041 addError(
1042 "A valid JSON document must be either an array or an object value.",
1043 token);
1044 return false;
1045 }
1046 }
1047 return successful;
1048}
1049
1050bool OurReader::readValue() {
1051 Token token;
1052 if (nodes_.size() > features_.stackLimit_) {
1053#if JSON_USE_EXCEPTION
1054 throwRuntimeError("Exceeded stackLimit in readValue().");
1055#else
1056 // throwRuntimeError aborts. Don't abort here.
1057 token.start_ = current_;
1058 token.end_ = current_;
1059 token.type_ = tokenError;
1060 return addError(
1061 "Exceeded stackLimit for nested object and/or array values.", token);
1062#endif
1063 }
1064 readTokenSkippingComments(token);
1065 bool successful = true;
1066
1067 if (collectComments_ && !commentsBefore_.empty()) {
1068 currentValue().setComment(commentsBefore_, commentBefore);
1069 commentsBefore_.clear();
1070 }
1071
1072 switch (token.type_) {
1073 case tokenObjectBegin:
1074 successful = readObject(token);
1075 currentValue().setOffsetLimit(current_ - begin_);
1076 break;
1077 case tokenArrayBegin:
1078 successful = readArray(token);
1079 currentValue().setOffsetLimit(current_ - begin_);
1080 break;
1081 case tokenNumber:
1082 successful = decodeNumber(token);
1083 break;
1084 case tokenString:
1085 successful = decodeString(token);
1086 break;
1087 case tokenTrue: {
1088 Value v(true);
1089 currentValue().swapPayload(v);
1090 currentValue().setOffsetStart(token.start_ - begin_);
1091 currentValue().setOffsetLimit(token.end_ - begin_);
1092 } break;
1093 case tokenFalse: {
1094 Value v(false);
1095 currentValue().swapPayload(v);
1096 currentValue().setOffsetStart(token.start_ - begin_);
1097 currentValue().setOffsetLimit(token.end_ - begin_);
1098 } break;
1099 case tokenNull: {
1100 Value v;
1101 currentValue().swapPayload(v);
1102 currentValue().setOffsetStart(token.start_ - begin_);
1103 currentValue().setOffsetLimit(token.end_ - begin_);
1104 } break;
1105 case tokenNaN: {
1106 Value v(std::numeric_limits<double>::quiet_NaN());
1107 currentValue().swapPayload(v);
1108 currentValue().setOffsetStart(token.start_ - begin_);
1109 currentValue().setOffsetLimit(token.end_ - begin_);
1110 } break;
1111 case tokenPosInf: {
1112 Value v(std::numeric_limits<double>::infinity());
1113 currentValue().swapPayload(v);
1114 currentValue().setOffsetStart(token.start_ - begin_);
1115 currentValue().setOffsetLimit(token.end_ - begin_);
1116 } break;
1117 case tokenNegInf: {
1118 Value v(-std::numeric_limits<double>::infinity());
1119 currentValue().swapPayload(v);
1120 currentValue().setOffsetStart(token.start_ - begin_);
1121 currentValue().setOffsetLimit(token.end_ - begin_);
1122 } break;
1123 case tokenArraySeparator:
1124 case tokenObjectEnd:
1125 case tokenArrayEnd:
1126 if (features_.allowDroppedNullPlaceholders_) {
1127 // "Un-read" the current token and mark the current value as a null
1128 // token.
1129 current_--;
1130 Value v;
1131 currentValue().swapPayload(v);
1132 currentValue().setOffsetStart(current_ - begin_ - 1);
1133 currentValue().setOffsetLimit(current_ - begin_);
1134 break;
1135 } // else, fall through ...
1136 default:
1137 currentValue().setOffsetStart(token.start_ - begin_);
1138 currentValue().setOffsetLimit(token.end_ - begin_);
1139 return addError("Syntax error: value, object or array expected.", token);
1140 }
1141
1142 if (collectComments_) {
1143 lastValueEnd_ = current_;
1144 lastValueHasAComment_ = false;
1145 lastValue_ = &currentValue();
1146 }
1147
1148 return successful;
1149}
1150
1151bool OurReader::readTokenSkippingComments(Token& token) {
1152 bool success = readToken(token);
1153 if (features_.allowComments_) {
1154 while (success && token.type_ == tokenComment) {
1155 success = readToken(token);
1156 }
1157 }
1158 return success;
1159}
1160
1161bool OurReader::readToken(Token& token) {
1162 skipSpaces();
1163 token.start_ = current_;
1164 Char c = getNextChar();
1165 bool ok = true;
1166 switch (c) {
1167 case '{':
1168 token.type_ = tokenObjectBegin;
1169 break;
1170 case '}':
1171 token.type_ = tokenObjectEnd;
1172 break;
1173 case '[':
1174 token.type_ = tokenArrayBegin;
1175 break;
1176 case ']':
1177 token.type_ = tokenArrayEnd;
1178 break;
1179 case '"':
1180 token.type_ = tokenString;
1181 ok = readString();
1182 break;
1183 case '\'':
1184 if (features_.allowSingleQuotes_) {
1185 token.type_ = tokenString;
1186 ok = readStringSingleQuote();
1187 } else {
1188 // If we don't allow single quotes, this is a failure case.
1189 ok = false;
1190 }
1191 break;
1192 case '/':
1193 token.type_ = tokenComment;
1194 ok = readComment();
1195 break;
1196 case '0':
1197 case '1':
1198 case '2':
1199 case '3':
1200 case '4':
1201 case '5':
1202 case '6':
1203 case '7':
1204 case '8':
1205 case '9':
1206 token.type_ = tokenNumber;
1207 readNumber(false);
1208 break;
1209 case '-':
1210 if (readNumber(true)) {
1211 token.type_ = tokenNumber;
1212 } else {
1213 token.type_ = tokenNegInf;
1214 ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1215 }
1216 break;
1217 case '+':
1218 if (readNumber(true)) {
1219 token.type_ = tokenNumber;
1220 } else {
1221 token.type_ = tokenPosInf;
1222 ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1223 }
1224 break;
1225 case 't':
1226 token.type_ = tokenTrue;
1227 ok = match("rue", 3);
1228 break;
1229 case 'f':
1230 token.type_ = tokenFalse;
1231 ok = match("alse", 4);
1232 break;
1233 case 'n':
1234 token.type_ = tokenNull;
1235 ok = match("ull", 3);
1236 break;
1237 case 'N':
1238 if (features_.allowSpecialFloats_) {
1239 token.type_ = tokenNaN;
1240 ok = match("aN", 2);
1241 } else {
1242 ok = false;
1243 }
1244 break;
1245 case 'I':
1246 if (features_.allowSpecialFloats_) {
1247 token.type_ = tokenPosInf;
1248 ok = match("nfinity", 7);
1249 } else {
1250 ok = false;
1251 }
1252 break;
1253 case ',':
1254 token.type_ = tokenArraySeparator;
1255 break;
1256 case ':':
1257 token.type_ = tokenMemberSeparator;
1258 break;
1259 case 0:
1260 token.type_ = tokenEndOfStream;
1261 break;
1262 default:
1263 ok = false;
1264 break;
1265 }
1266 if (!ok)
1267 token.type_ = tokenError;
1268 token.end_ = current_;
1269 return ok;
1270}
1271
1272void OurReader::skipSpaces() {
1273 while (current_ != end_) {
1274 Char c = *current_;
1275 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1276 ++current_;
1277 else
1278 break;
1279 }
1280}
1281
1282// Skip whitespace and any comments, leaving current_ at the next significant
1283// character. Consumed comments are recorded (commentsBefore_) so the next value
1284// still receives them; if none follows they are simply not attached. This lets
1285// callers peek for a delimiter that is preceded by comments (e.g. a ']' after a
1286// trailing comma -- see readArray and issue #1500).
1287void OurReader::skipCommentTokens() {
1288 skipSpaces();
1289 if (!features_.allowComments_)
1290 return;
1291 while (current_ != end_ && *current_ == '/' && (current_ + 1) != end_ &&
1292 (current_[1] == '/' || current_[1] == '*')) {
1293 Token comment;
1294 if (!readToken(comment))
1295 return;
1296 skipSpaces();
1297 }
1298}
1299
1300void OurReader::skipBom(bool skipBom) {
1301 // The default behavior is to skip BOM.
1302 if (skipBom) {
1303 if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
1304 begin_ += 3;
1305 current_ = begin_;
1306 }
1307 }
1308}
1309
1310bool OurReader::match(const Char* pattern, int patternLength) {
1311 if (end_ - current_ < patternLength)
1312 return false;
1313 int index = patternLength;
1314 while (index--)
1315 if (current_[index] != pattern[index])
1316 return false;
1317 current_ += patternLength;
1318 return true;
1319}
1320
1321bool OurReader::readComment() {
1322 const Location commentBegin = current_ - 1;
1323 const Char c = getNextChar();
1324 bool successful = false;
1325 bool cStyleWithEmbeddedNewline = false;
1326
1327 const bool isCStyleComment = (c == '*');
1328 const bool isCppStyleComment = (c == '/');
1329 if (isCStyleComment) {
1330 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1331 } else if (isCppStyleComment) {
1332 successful = readCppStyleComment();
1333 }
1334
1335 if (!successful)
1336 return false;
1337
1338 if (collectComments_) {
1339 CommentPlacement placement = commentBefore;
1340
1341 if (!lastValueHasAComment_) {
1342 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1343 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1344 placement = commentAfterOnSameLine;
1345 }
1346 }
1347 // The gap between the last value and this comment only grows as more
1348 // comments are consumed, so a later comment can never be on the same
1349 // line as that value. Mark it handled to avoid re-scanning the same
1350 // growing prefix for every following comment (quadratic behavior).
1351 lastValueHasAComment_ = true;
1352 }
1353
1354 addComment(commentBegin, current_, placement);
1355 }
1356 return true;
1357}
1358
1359String OurReader::normalizeEOL(OurReader::Location begin,
1360 OurReader::Location end) {
1361 String normalized;
1362 normalized.reserve(static_cast<size_t>(end - begin));
1363 OurReader::Location current = begin;
1364 while (current != end) {
1365 char c = *current++;
1366 if (c == '\r') {
1367 if (current != end && *current == '\n')
1368 // convert dos EOL
1369 ++current;
1370 // convert Mac EOL
1371 normalized += '\n';
1372 } else {
1373 normalized += c;
1374 }
1375 }
1376 return normalized;
1377}
1378
1379void OurReader::addComment(Location begin, Location end,
1380 CommentPlacement placement) {
1381 assert(collectComments_);
1382 const String& normalized = normalizeEOL(begin, end);
1383 if (placement == commentAfterOnSameLine) {
1384 assert(lastValue_ != nullptr);
1385 lastValue_->setComment(normalized, placement);
1386 } else {
1387 commentsBefore_ += normalized;
1388 }
1389}
1390
1391bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1392 *containsNewLineResult = false;
1393
1394 while ((current_ + 1) < end_) {
1395 Char c = getNextChar();
1396 if (c == '*' && *current_ == '/')
1397 break;
1398 if (c == '\n')
1399 *containsNewLineResult = true;
1400 }
1401
1402 return getNextChar() == '/';
1403}
1404
1405bool OurReader::readCppStyleComment() {
1406 while (current_ != end_) {
1407 Char c = getNextChar();
1408 if (c == '\n')
1409 break;
1410 if (c == '\r') {
1411 // Consume DOS EOL. It will be normalized in addComment.
1412 if (current_ != end_ && *current_ == '\n')
1413 getNextChar();
1414 // Break on Moc OS 9 EOL.
1415 break;
1416 }
1417 }
1418 return true;
1419}
1420
1421bool OurReader::readNumber(bool checkInf) {
1422 Location p = current_;
1423 if (checkInf && p != end_ && *p == 'I') {
1424 current_ = ++p;
1425 return false;
1426 }
1427 char c = '0'; // stopgap for already consumed character
1428 // integral part
1429 while (c >= '0' && c <= '9')
1430 c = (current_ = p) < end_ ? *p++ : '\0';
1431 // fractional part
1432 if (c == '.') {
1433 c = (current_ = p) < end_ ? *p++ : '\0';
1434 while (c >= '0' && c <= '9')
1435 c = (current_ = p) < end_ ? *p++ : '\0';
1436 }
1437 // exponential part
1438 if (c == 'e' || c == 'E') {
1439 c = (current_ = p) < end_ ? *p++ : '\0';
1440 if (c == '+' || c == '-')
1441 c = (current_ = p) < end_ ? *p++ : '\0';
1442 while (c >= '0' && c <= '9')
1443 c = (current_ = p) < end_ ? *p++ : '\0';
1444 }
1445 return true;
1446}
1447bool OurReader::readString() {
1448 Char c = 0;
1449 while (current_ != end_) {
1450 c = getNextChar();
1451 if (c == '\\')
1452 getNextChar();
1453 else if (c == '"')
1454 break;
1455 }
1456 return c == '"';
1457}
1458
1459bool OurReader::readStringSingleQuote() {
1460 Char c = 0;
1461 while (current_ != end_) {
1462 c = getNextChar();
1463 if (c == '\\')
1464 getNextChar();
1465 else if (c == '\'')
1466 break;
1467 }
1468 return c == '\'';
1469}
1470
1471bool OurReader::readObject(Token& token) {
1472 Token tokenName;
1473 String name;
1474 Value init(objectValue);
1475 currentValue().swapPayload(init);
1476 currentValue().setOffsetStart(token.start_ - begin_);
1477 while (readTokenSkippingComments(tokenName)) {
1478 if (tokenName.type_ == tokenObjectEnd &&
1479 (name.empty() ||
1480 features_.allowTrailingCommas_)) // empty object or trailing comma
1481 return true;
1482 name.clear();
1483 if (tokenName.type_ == tokenString) {
1484 if (!decodeString(tokenName, name))
1485 return recoverFromError(tokenObjectEnd);
1486 } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1487 Value numberName;
1488 if (!decodeNumber(tokenName, numberName))
1489 return recoverFromError(tokenObjectEnd);
1490 name = numberName.asString();
1491 } else {
1492 break;
1493 }
1494 if (name.length() >= (1U << 30))
1495 throwRuntimeError("keylength >= 2^30");
1496 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1497 String msg = "Duplicate key: '" + name + "'";
1498 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1499 }
1500
1501 Token colon;
1502 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1503 return addErrorAndRecover("Missing ':' after object member name", colon,
1504 tokenObjectEnd);
1505 }
1506 Value& value = currentValue()[name];
1507 nodes_.push(&value);
1508 bool ok = readValue();
1509 nodes_.pop();
1510 if (!ok) // error already set
1511 return recoverFromError(tokenObjectEnd);
1512
1513 Token comma;
1514 if (!readTokenSkippingComments(comma) ||
1515 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
1516 return addErrorAndRecover("Missing ',' or '}' in object declaration",
1517 comma, tokenObjectEnd);
1518 }
1519 if (comma.type_ == tokenObjectEnd)
1520 return true;
1521 }
1522 return addErrorAndRecover("Missing '}' or object member name", tokenName,
1523 tokenObjectEnd);
1524}
1525
1526bool OurReader::readArray(Token& token) {
1527 Value init(arrayValue);
1528 currentValue().swapPayload(init);
1529 currentValue().setOffsetStart(token.start_ - begin_);
1530 int index = 0;
1531 for (;;) {
1532 // Skip comments too, so a ']' that follows a trailing comma (or comments in
1533 // an otherwise empty array) is recognized rather than mistaken for the
1534 // start of another value. See issue #1500.
1535 skipCommentTokens();
1536 if (current_ != end_ && *current_ == ']' &&
1537 (index == 0 ||
1538 (features_.allowTrailingCommas_ &&
1539 !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
1540 // comma
1541 {
1542 Token endArray;
1543 readToken(endArray);
1544 return true;
1545 }
1546 Value& value = currentValue()[index++];
1547 nodes_.push(&value);
1548 bool ok = readValue();
1549 nodes_.pop();
1550 if (!ok) // error already set
1551 return recoverFromError(tokenArrayEnd);
1552
1553 Token currentToken;
1554 // Accept Comment after last item in the array.
1555 ok = readTokenSkippingComments(currentToken);
1556 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1557 currentToken.type_ != tokenArrayEnd);
1558 if (!ok || badTokenType) {
1559 return addErrorAndRecover("Missing ',' or ']' in array declaration",
1560 currentToken, tokenArrayEnd);
1561 }
1562 if (currentToken.type_ == tokenArrayEnd)
1563 break;
1564 }
1565 return true;
1566}
1567
1568bool OurReader::decodeNumber(Token& token) {
1569 Value decoded;
1570 if (!decodeNumber(token, decoded))
1571 return false;
1572 currentValue().swapPayload(decoded);
1573 currentValue().setOffsetStart(token.start_ - begin_);
1574 currentValue().setOffsetLimit(token.end_ - begin_);
1575 return true;
1576}
1577
1578bool OurReader::decodeNumber(Token& token, Value& decoded) {
1579 // Attempts to parse the number as an integer. If the number is
1580 // larger than the maximum supported value of an integer then
1581 // we decode the number as a double.
1582 Location current = token.start_;
1583 const bool isNegative = *current == '-';
1584 if (isNegative) {
1585 ++current;
1586 }
1587
1588 // We assume we can represent the largest and smallest integer types as
1589 // unsigned integers with separate sign. This is only true if they can fit
1590 // into an unsigned integer.
1591 static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1592 "Int must be smaller than UInt");
1593
1594 // We need to convert minLargestInt into a positive number. The easiest way
1595 // to do this conversion is to assume our "threshold" value of minLargestInt
1596 // divided by 10 can fit in maxLargestInt when absolute valued. This should
1597 // be a safe assumption.
1598 static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1599 "The absolute value of minLargestInt must be greater than or "
1600 "equal to maxLargestInt");
1601 static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1602 "The absolute value of minLargestInt must be only 1 magnitude "
1603 "larger than maxLargest Int");
1604
1605 static constexpr Value::LargestUInt positive_threshold =
1606 Value::maxLargestUInt / 10;
1607 static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1608
1609 // For the negative values, we have to be more careful. Since typically
1610 // -Value::minLargestInt will cause an overflow, we first divide by 10 and
1611 // then take the inverse. This assumes that minLargestInt is only a single
1612 // power of 10 different in magnitude, which we check above. For the last
1613 // digit, we take the modulus before negating for the same reason.
1614 static constexpr auto negative_threshold =
1615 Value::LargestUInt(-(Value::minLargestInt / 10));
1616 static constexpr auto negative_last_digit =
1617 Value::UInt(-(Value::minLargestInt % 10));
1618
1619 const Value::LargestUInt threshold =
1620 isNegative ? negative_threshold : positive_threshold;
1621 const Value::UInt max_last_digit =
1622 isNegative ? negative_last_digit : positive_last_digit;
1623
1624 Value::LargestUInt value = 0;
1625 while (current < token.end_) {
1626 Char c = *current++;
1627 if (c < '0' || c > '9')
1628 return decodeDouble(token, decoded);
1629
1630 const auto digit(static_cast<Value::UInt>(c - '0'));
1631 if (value >= threshold) {
1632 // We've hit or exceeded the max value divided by 10 (rounded down). If
1633 // a) we've only just touched the limit, meaning value == threshold,
1634 // b) this is the last digit, or
1635 // c) it's small enough to fit in that rounding delta, we're okay.
1636 // Otherwise treat this number as a double to avoid overflow.
1637 if (value > threshold || current != token.end_ ||
1638 digit > max_last_digit) {
1639 return decodeDouble(token, decoded);
1640 }
1641 }
1642 value = value * 10 + digit;
1643 }
1644
1645 if (isNegative) {
1646 // We use the same magnitude assumption here, just in case.
1647 const auto last_digit = static_cast<Value::UInt>(value % 10);
1648 decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
1649 } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
1650 decoded = Value::LargestInt(value);
1651 } else {
1652 decoded = value;
1653 }
1654
1655 return true;
1656}
1657
1658bool OurReader::decodeDouble(Token& token) {
1659 Value decoded;
1660 if (!decodeDouble(token, decoded))
1661 return false;
1662 currentValue().swapPayload(decoded);
1663 currentValue().setOffsetStart(token.start_ - begin_);
1664 currentValue().setOffsetLimit(token.end_ - begin_);
1665 return true;
1666}
1667
1668bool OurReader::decodeDouble(Token& token, Value& decoded) {
1669 double value = 0;
1670 IStringStream is(String(token.start_, token.end_));
1671 is.imbue(std::locale::classic());
1672 if (!(is >> value)) {
1673 if (value == std::numeric_limits<double>::max())
1674 value = std::numeric_limits<double>::infinity();
1675 else if (value == std::numeric_limits<double>::lowest())
1676 value = -std::numeric_limits<double>::infinity();
1677 // operator>> sets failbit for a subnormal result (underflow) even though
1678 // it produced the correctly-rounded value, which made such numbers fail to
1679 // parse back after jsoncpp serialized them. Keep a subnormal value instead
1680 // of rejecting it. See issue #1427. Other failures -- malformed numbers
1681 // like "0e" or "0e+", or non-numbers -- leave the value at zero/non-finite
1682 // and are still rejected.
1683 else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
1684 return addError(
1685 "'" + String(token.start_, token.end_) + "' is not a number.", token);
1686 }
1687 decoded = value;
1688 return true;
1689}
1690
1691bool OurReader::decodeString(Token& token) {
1692 String decoded_string;
1693 if (!decodeString(token, decoded_string))
1694 return false;
1695 Value decoded(decoded_string);
1696 currentValue().swapPayload(decoded);
1697 currentValue().setOffsetStart(token.start_ - begin_);
1698 currentValue().setOffsetLimit(token.end_ - begin_);
1699 return true;
1700}
1701
1702bool OurReader::decodeString(Token& token, String& decoded) {
1703 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
1704 Location current = token.start_ + 1; // skip '"'
1705 Location end = token.end_ - 1; // do not include '"'
1706 while (current != end) {
1707 Char c = *current++;
1708 if (c == '"')
1709 break;
1710 if (c == '\\') {
1711 if (current == end)
1712 return addError("Empty escape sequence in string", token, current);
1713 Char escape = *current++;
1714 switch (escape) {
1715 case '"':
1716 decoded += '"';
1717 break;
1718 case '/':
1719 decoded += '/';
1720 break;
1721 case '\\':
1722 decoded += '\\';
1723 break;
1724 case 'b':
1725 decoded += '\b';
1726 break;
1727 case 'f':
1728 decoded += '\f';
1729 break;
1730 case 'n':
1731 decoded += '\n';
1732 break;
1733 case 'r':
1734 decoded += '\r';
1735 break;
1736 case 't':
1737 decoded += '\t';
1738 break;
1739 case 'u': {
1740 unsigned int unicode;
1741 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1742 return false;
1743 decoded += codePointToUTF8(unicode);
1744 } break;
1745 default:
1746 return addError("Bad escape sequence in string", token, current);
1747 }
1748 } else {
1749 if (static_cast<unsigned char>(c) < 0x20)
1750 return addError("Control character in string", token, current - 1);
1751 decoded += c;
1752 }
1753 }
1754 return true;
1755}
1756
1757bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1758 Location end, unsigned int& unicode) {
1759
1760 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1761 return false;
1762 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1763 // surrogate pairs
1764 if (end - current < 6)
1765 return addError(
1766 "additional six characters expected to parse unicode surrogate pair.",
1767 token, current);
1768 if (*(current++) == '\\' && *(current++) == 'u') {
1769 unsigned int surrogatePair;
1770 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1771 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1772 } else
1773 return false;
1774 } else
1775 return addError("expecting another \\u token to begin the second half of "
1776 "a unicode surrogate pair",
1777 token, current);
1778 }
1779 return true;
1780}
1781
1782bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1783 Location end,
1784 unsigned int& ret_unicode) {
1785 if (end - current < 4)
1786 return addError(
1787 "Bad unicode escape sequence in string: four digits expected.", token,
1788 current);
1789 int unicode = 0;
1790 for (int index = 0; index < 4; ++index) {
1791 Char c = *current++;
1792 unicode *= 16;
1793 if (c >= '0' && c <= '9')
1794 unicode += c - '0';
1795 else if (c >= 'a' && c <= 'f')
1796 unicode += c - 'a' + 10;
1797 else if (c >= 'A' && c <= 'F')
1798 unicode += c - 'A' + 10;
1799 else
1800 return addError(
1801 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1802 token, current);
1803 }
1804 ret_unicode = static_cast<unsigned int>(unicode);
1805 return true;
1806}
1807
1808bool OurReader::addError(const String& message, Token& token, Location extra) {
1809 ErrorInfo info;
1810 info.token_ = token;
1811 info.message_ = message;
1812 info.extra_ = extra;
1813 errors_.push_back(info);
1814 return false;
1815}
1816
1817bool OurReader::recoverFromError(TokenType skipUntilToken) {
1818 size_t errorCount = errors_.size();
1819 Token skip;
1820 for (;;) {
1821 if (!readToken(skip))
1822 errors_.resize(errorCount); // discard errors caused by recovery
1823 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1824 break;
1825 }
1826 errors_.resize(errorCount);
1827 return false;
1828}
1829
1830bool OurReader::addErrorAndRecover(const String& message, Token& token,
1831 TokenType skipUntilToken) {
1832 addError(message, token);
1833 return recoverFromError(skipUntilToken);
1834}
1835
1836Value& OurReader::currentValue() { return *(nodes_.top()); }
1837
1838OurReader::Char OurReader::getNextChar() {
1839 if (current_ == end_)
1840 return 0;
1841 return *current_++;
1842}
1843
1844void OurReader::getLocationLineAndColumn(Location location, int& line,
1845 int& column) const {
1846 Location current = begin_;
1847 Location lastLineStart = current;
1848 line = 0;
1849 while (current < location && current != end_) {
1850 Char c = *current++;
1851 if (c == '\r') {
1852 if (current != end_ && *current == '\n')
1853 ++current;
1854 lastLineStart = current;
1855 ++line;
1856 } else if (c == '\n') {
1857 lastLineStart = current;
1858 ++line;
1859 }
1860 }
1861 // column & line start at 1
1862 column = int(location - lastLineStart) + 1;
1863 ++line;
1864}
1865
1866String OurReader::getLocationLineAndColumn(Location location) const {
1867 int line, column;
1868 getLocationLineAndColumn(location, line, column);
1869 char buffer[18 + 16 + 16 + 1];
1870 jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1871 return buffer;
1872}
1873
1874String OurReader::getFormattedErrorMessages() const {
1875 String formattedMessage;
1876 for (const auto& error : errors_) {
1877 formattedMessage +=
1878 "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
1879 formattedMessage += " " + error.message_ + "\n";
1880 if (error.extra_)
1881 formattedMessage +=
1882 "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
1883 }
1884 return formattedMessage;
1885}
1886
1887std::vector<CharReader::StructuredError>
1888OurReader::getStructuredErrors() const {
1889 std::vector<CharReader::StructuredError> allErrors;
1890 for (const auto& error : errors_) {
1891 CharReader::StructuredError structured;
1892 structured.offset_start = error.token_.start_ - begin_;
1893 structured.offset_limit = error.token_.end_ - begin_;
1894 structured.message = error.message_;
1895 allErrors.push_back(structured);
1896 }
1897 return allErrors;
1898}
1899
1900class OurCharReader : public CharReader {
1901
1902public:
1903 OurCharReader(bool collectComments, OurFeatures const& features)
1904 : CharReader(
1905 std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1906
1907protected:
1908 class OurImpl : public Impl {
1909 public:
1910 OurImpl(bool collectComments, OurFeatures const& features)
1911 : collectComments_(collectComments), reader_(features) {}
1912
1913 bool parse(char const* beginDoc, char const* endDoc, Value* root,
1914 String* errs) override {
1915 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1916 if (errs) {
1917 *errs = reader_.getFormattedErrorMessages();
1918 }
1919 return ok;
1920 }
1921
1922 std::vector<CharReader::StructuredError>
1923 getStructuredErrors() const override {
1924 return reader_.getStructuredErrors();
1925 }
1926
1927 private:
1928 bool const collectComments_;
1929 OurReader reader_;
1930 };
1931};
1932
1936 bool collectComments = settings_["collectComments"].asBool();
1937 OurFeatures features = OurFeatures::all();
1938 features.allowComments_ = settings_["allowComments"].asBool();
1939 features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
1940 features.strictRoot_ = settings_["strictRoot"].asBool();
1941 features.allowDroppedNullPlaceholders_ =
1942 settings_["allowDroppedNullPlaceholders"].asBool();
1943 features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
1944 features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
1945
1946 // Stack limit is always a size_t, so we get this as an unsigned int
1947 // regardless of it we have 64-bit integer support enabled.
1948 features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
1949 features.failIfExtra_ = settings_["failIfExtra"].asBool();
1950 features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
1951 features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
1952 features.skipBom_ = settings_["skipBom"].asBool();
1953 return new OurCharReader(collectComments, features);
1954}
1955
1957 static const auto& valid_keys = *new std::set<String>{
1958 "collectComments",
1959 "allowComments",
1960 "allowTrailingCommas",
1961 "strictRoot",
1962 "allowDroppedNullPlaceholders",
1963 "allowNumericKeys",
1964 "allowSingleQuotes",
1965 "stackLimit",
1966 "failIfExtra",
1967 "rejectDupKeys",
1968 "allowSpecialFloats",
1969 "skipBom",
1970 };
1971 for (auto si = settings_.begin(); si != settings_.end(); ++si) {
1972 auto key = si.name();
1973 if (valid_keys.count(key))
1974 continue;
1975 if (invalid)
1976 (*invalid)[key] = *si;
1977 else
1978 return false;
1979 }
1980 return invalid ? invalid->empty() : true;
1981}
1982
1984 return settings_[key];
1985}
1986// static
1989 (*settings)["allowComments"] = false;
1990 (*settings)["allowTrailingCommas"] = false;
1991 (*settings)["strictRoot"] = true;
1992 (*settings)["allowDroppedNullPlaceholders"] = false;
1993 (*settings)["allowNumericKeys"] = false;
1994 (*settings)["allowSingleQuotes"] = false;
1995 (*settings)["stackLimit"] = 256;
1996 (*settings)["failIfExtra"] = true;
1997 (*settings)["rejectDupKeys"] = true;
1998 (*settings)["allowSpecialFloats"] = false;
1999 (*settings)["skipBom"] = true;
2001}
2002// static
2005 (*settings)["collectComments"] = true;
2006 (*settings)["allowComments"] = true;
2007 (*settings)["allowTrailingCommas"] = true;
2008 (*settings)["strictRoot"] = false;
2009 (*settings)["allowDroppedNullPlaceholders"] = false;
2010 (*settings)["allowNumericKeys"] = false;
2011 (*settings)["allowSingleQuotes"] = false;
2012 (*settings)["stackLimit"] = 256;
2013 (*settings)["failIfExtra"] = false;
2014 (*settings)["rejectDupKeys"] = false;
2015 (*settings)["allowSpecialFloats"] = false;
2016 (*settings)["skipBom"] = true;
2018}
2019// static
2022 (*settings)["allowComments"] = false;
2023 (*settings)["allowTrailingCommas"] = false;
2024 (*settings)["strictRoot"] = false;
2025 (*settings)["allowDroppedNullPlaceholders"] = false;
2026 (*settings)["allowNumericKeys"] = false;
2027 (*settings)["allowSingleQuotes"] = false;
2028 (*settings)["stackLimit"] = 256;
2029 (*settings)["failIfExtra"] = true;
2030 (*settings)["rejectDupKeys"] = false;
2031 (*settings)["allowSpecialFloats"] = false;
2032 (*settings)["skipBom"] = false;
2034}
2035
2036std::vector<CharReader::StructuredError>
2038 return _impl->getStructuredErrors();
2039}
2040
2041bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
2042 String* errs) {
2043 return _impl->parse(beginDoc, endDoc, root, errs);
2044}
2045
2047// global functions
2048
2049bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
2050 String* errs) {
2051 OStringStream ssin;
2052 ssin << sin.rdbuf();
2053 String doc = std::move(ssin).str();
2054 char const* begin = doc.data();
2055 char const* end = begin + doc.size();
2056 // Note that we do not actually need a null-terminator.
2057 CharReaderPtr const reader(fact.newCharReader());
2058 return reader->parse(begin, end, root, errs);
2059}
2060
2063 String errs;
2064 bool ok = parseFromStream(b, sin, &root, &errs);
2065 if (!ok) {
2066 throwRuntimeError(errs);
2067 }
2068 return sin;
2069}
2070
2071} // namespace Json
virtual CharReader * newCharReader() const =0
Allocate a CharReader via operator new().
Build a CharReader implementation.
Definition reader.h:317
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
static void ecma404Mode(Json::Value *settings)
ECMA-404 mode.
Value & operator[](const String &key)
A simple way to update a specific setting.
CharReader * newCharReader() const override
Allocate a CharReader via operator new().
static void strictMode(Json::Value *settings)
Same as old Features::strictMode().
Json::Value settings_
Configuration of this builder.
Definition reader.h:363
~CharReaderBuilder() override
bool validate(Json::Value *invalid) const
Interface for reading JSON from a char array.
Definition reader.h:248
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
virtual bool parse(char const *beginDoc, char const *endDoc, Value *root, String *errs)
Read a Value from a JSON document.
Configuration passed to reader and writer.
bool strictRoot_
true if root must be either an array or an object value.
bool allowComments_
true if comments are allowed. Default: true.
bool allowDroppedNullPlaceholders_
true if dropped null placeholders are allowed. Default: false.
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
Features()
Initialize the configuration like JsonConfig::allFeatures;.
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
bool allowNumericKeys_
true if numeric object key are allowed. Default: false.
char Char
Definition reader.h:39
Reader()
Constructs a Reader allowing all features for parsing.
bool pushError(const Value &value, const String &message)
Add a semantic error message.
bool good() const
Return whether there are any errors.
const Char * Location
Definition reader.h:40
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
String getFormatedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
String getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
Represents a JSON value.
Definition value.h:207
bool empty() const
Return true if empty array, empty object, or null; otherwise, false.
static constexpr LargestInt maxLargestInt
Maximum signed integer value that can be stored in a Json::Value.
Definition value.h:241
Json::UInt UInt
Definition value.h:215
bool isArray() const
void setComment(const char *comment, CommentPlacement placement)
Definition value.h:666
ptrdiff_t getOffsetLimit() const
void swapPayload(Value &other)
Swap values but leave comments and source offsets in place.
void setOffsetLimit(ptrdiff_t limit)
Json::LargestInt LargestInt
Definition value.h:221
Json::LargestUInt LargestUInt
Definition value.h:222
bool isObject() const
void setOffsetStart(ptrdiff_t start)
static constexpr Int maxInt
Maximum signed int value that can be stored in a Json::Value.
Definition value.h:248
static constexpr LargestUInt maxLargestUInt
Maximum unsigned integer value that can be stored in a Json::Value.
Definition value.h:243
static constexpr LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
Definition value.h:238
ptrdiff_t getOffsetStart() const
#define jsoncpp_snprintf
If defined, indicates that the source file is amalgamated to prevent private header inclusion.
Definition config.h:65
#define JSONCPP_DEPRECATED_STACK_LIMIT
static size_t const stackLimit_g
JSON (JavaScript Object Notation).
Definition allocator.h:16
size_t & newlineScanByteCountForTesting()
CommentPlacement
Definition value.h:132
@ commentAfterOnSameLine
a comment just after a value on the same line
Definition value.h:134
@ commentBefore
a comment placed on the line before a value
Definition value.h:133
@ commentAfter
a comment on the line after a value (only make sense for
Definition value.h:135
std::basic_istringstream< String::value_type, String::traits_type, String::allocator_type > IStringStream
Definition config.h:136
std::unique_ptr< CharReader > CharReaderPtr
@ arrayValue
array value (ordered list)
Definition value.h:128
@ objectValue
object value (collection of name/value pairs).
Definition value.h:129
void throwRuntimeError(String const &msg)
used internally
std::istream IStream
Definition config.h:142
std::basic_string< char, std::char_traits< char >, Allocator< char > > String
Definition config.h:135
IStream & operator>>(IStream &, Value &)
Read from 'sin' into 'root'.
bool parseFromStream(CharReader::Factory const &, IStream &, Value *root, String *errs)
Consume entire stream and use its begin/end.
std::basic_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
Definition config.h:139
An error tagged with where in the JSON text it was encountered.
Definition reader.h:47