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.
991JSON_API size_t& newlineScanByteCountForTesting();
993 static thread_local size_t count = 0;
994 return count;
995}
996
997bool OurReader::containsNewLine(OurReader::Location begin,
998 OurReader::Location end) {
999 newlineScanByteCountForTesting() += static_cast<size_t>(end - begin);
1000 return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
1001}
1002
1003OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1004
1005bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
1006 bool collectComments) {
1007 if (!features_.allowComments_) {
1008 collectComments = false;
1009 }
1010
1011 begin_ = beginDoc;
1012 end_ = endDoc;
1013 collectComments_ = collectComments;
1014 current_ = begin_;
1015 lastValueEnd_ = nullptr;
1016 lastValue_ = nullptr;
1017 commentsBefore_.clear();
1018 errors_.clear();
1019 while (!nodes_.empty())
1020 nodes_.pop();
1021 nodes_.push(&root);
1022
1023 // skip byte order mark if it exists at the beginning of the UTF-8 text.
1024 skipBom(features_.skipBom_);
1025 bool successful = readValue();
1026 nodes_.pop();
1027 Token token;
1028 readTokenSkippingComments(token);
1029 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1030 addError("Extra non-whitespace after JSON value.", token);
1031 return false;
1032 }
1033 if (collectComments_ && !commentsBefore_.empty())
1034 root.setComment(commentsBefore_, commentAfter);
1035 if (features_.strictRoot_) {
1036 if (!root.isArray() && !root.isObject()) {
1037 // Set error location to start of doc, ideally should be first token found
1038 // in doc
1039 token.type_ = tokenError;
1040 token.start_ = beginDoc;
1041 token.end_ = endDoc;
1042 addError(
1043 "A valid JSON document must be either an array or an object value.",
1044 token);
1045 return false;
1046 }
1047 }
1048 return successful;
1049}
1050
1051bool OurReader::readValue() {
1052 Token token;
1053 if (nodes_.size() > features_.stackLimit_) {
1054#if JSON_USE_EXCEPTION
1055 throwRuntimeError("Exceeded stackLimit in readValue().");
1056#else
1057 // throwRuntimeError aborts. Don't abort here.
1058 token.start_ = current_;
1059 token.end_ = current_;
1060 token.type_ = tokenError;
1061 return addError(
1062 "Exceeded stackLimit for nested object and/or array values.", token);
1063#endif
1064 }
1065 readTokenSkippingComments(token);
1066 bool successful = true;
1067
1068 if (collectComments_ && !commentsBefore_.empty()) {
1069 currentValue().setComment(commentsBefore_, commentBefore);
1070 commentsBefore_.clear();
1071 }
1072
1073 switch (token.type_) {
1074 case tokenObjectBegin:
1075 successful = readObject(token);
1076 currentValue().setOffsetLimit(current_ - begin_);
1077 break;
1078 case tokenArrayBegin:
1079 successful = readArray(token);
1080 currentValue().setOffsetLimit(current_ - begin_);
1081 break;
1082 case tokenNumber:
1083 successful = decodeNumber(token);
1084 break;
1085 case tokenString:
1086 successful = decodeString(token);
1087 break;
1088 case tokenTrue: {
1089 Value v(true);
1090 currentValue().swapPayload(v);
1091 currentValue().setOffsetStart(token.start_ - begin_);
1092 currentValue().setOffsetLimit(token.end_ - begin_);
1093 } break;
1094 case tokenFalse: {
1095 Value v(false);
1096 currentValue().swapPayload(v);
1097 currentValue().setOffsetStart(token.start_ - begin_);
1098 currentValue().setOffsetLimit(token.end_ - begin_);
1099 } break;
1100 case tokenNull: {
1101 Value v;
1102 currentValue().swapPayload(v);
1103 currentValue().setOffsetStart(token.start_ - begin_);
1104 currentValue().setOffsetLimit(token.end_ - begin_);
1105 } break;
1106 case tokenNaN: {
1107 Value v(std::numeric_limits<double>::quiet_NaN());
1108 currentValue().swapPayload(v);
1109 currentValue().setOffsetStart(token.start_ - begin_);
1110 currentValue().setOffsetLimit(token.end_ - begin_);
1111 } break;
1112 case tokenPosInf: {
1113 Value v(std::numeric_limits<double>::infinity());
1114 currentValue().swapPayload(v);
1115 currentValue().setOffsetStart(token.start_ - begin_);
1116 currentValue().setOffsetLimit(token.end_ - begin_);
1117 } break;
1118 case tokenNegInf: {
1119 Value v(-std::numeric_limits<double>::infinity());
1120 currentValue().swapPayload(v);
1121 currentValue().setOffsetStart(token.start_ - begin_);
1122 currentValue().setOffsetLimit(token.end_ - begin_);
1123 } break;
1124 case tokenArraySeparator:
1125 case tokenObjectEnd:
1126 case tokenArrayEnd:
1127 if (features_.allowDroppedNullPlaceholders_) {
1128 // "Un-read" the current token and mark the current value as a null
1129 // token.
1130 current_--;
1131 Value v;
1132 currentValue().swapPayload(v);
1133 currentValue().setOffsetStart(current_ - begin_ - 1);
1134 currentValue().setOffsetLimit(current_ - begin_);
1135 break;
1136 } // else, fall through ...
1137 default:
1138 currentValue().setOffsetStart(token.start_ - begin_);
1139 currentValue().setOffsetLimit(token.end_ - begin_);
1140 return addError("Syntax error: value, object or array expected.", token);
1141 }
1142
1143 if (collectComments_) {
1144 lastValueEnd_ = current_;
1145 lastValueHasAComment_ = false;
1146 lastValue_ = &currentValue();
1147 }
1148
1149 return successful;
1150}
1151
1152bool OurReader::readTokenSkippingComments(Token& token) {
1153 bool success = readToken(token);
1154 if (features_.allowComments_) {
1155 while (success && token.type_ == tokenComment) {
1156 success = readToken(token);
1157 }
1158 }
1159 return success;
1160}
1161
1162bool OurReader::readToken(Token& token) {
1163 skipSpaces();
1164 token.start_ = current_;
1165 Char c = getNextChar();
1166 bool ok = true;
1167 switch (c) {
1168 case '{':
1169 token.type_ = tokenObjectBegin;
1170 break;
1171 case '}':
1172 token.type_ = tokenObjectEnd;
1173 break;
1174 case '[':
1175 token.type_ = tokenArrayBegin;
1176 break;
1177 case ']':
1178 token.type_ = tokenArrayEnd;
1179 break;
1180 case '"':
1181 token.type_ = tokenString;
1182 ok = readString();
1183 break;
1184 case '\'':
1185 if (features_.allowSingleQuotes_) {
1186 token.type_ = tokenString;
1187 ok = readStringSingleQuote();
1188 } else {
1189 // If we don't allow single quotes, this is a failure case.
1190 ok = false;
1191 }
1192 break;
1193 case '/':
1194 token.type_ = tokenComment;
1195 ok = readComment();
1196 break;
1197 case '0':
1198 case '1':
1199 case '2':
1200 case '3':
1201 case '4':
1202 case '5':
1203 case '6':
1204 case '7':
1205 case '8':
1206 case '9':
1207 token.type_ = tokenNumber;
1208 readNumber(false);
1209 break;
1210 case '-':
1211 if (readNumber(true)) {
1212 token.type_ = tokenNumber;
1213 } else {
1214 token.type_ = tokenNegInf;
1215 ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1216 }
1217 break;
1218 case '+':
1219 if (readNumber(true)) {
1220 token.type_ = tokenNumber;
1221 } else {
1222 token.type_ = tokenPosInf;
1223 ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1224 }
1225 break;
1226 case 't':
1227 token.type_ = tokenTrue;
1228 ok = match("rue", 3);
1229 break;
1230 case 'f':
1231 token.type_ = tokenFalse;
1232 ok = match("alse", 4);
1233 break;
1234 case 'n':
1235 token.type_ = tokenNull;
1236 ok = match("ull", 3);
1237 break;
1238 case 'N':
1239 if (features_.allowSpecialFloats_) {
1240 token.type_ = tokenNaN;
1241 ok = match("aN", 2);
1242 } else {
1243 ok = false;
1244 }
1245 break;
1246 case 'I':
1247 if (features_.allowSpecialFloats_) {
1248 token.type_ = tokenPosInf;
1249 ok = match("nfinity", 7);
1250 } else {
1251 ok = false;
1252 }
1253 break;
1254 case ',':
1255 token.type_ = tokenArraySeparator;
1256 break;
1257 case ':':
1258 token.type_ = tokenMemberSeparator;
1259 break;
1260 case 0:
1261 token.type_ = tokenEndOfStream;
1262 break;
1263 default:
1264 ok = false;
1265 break;
1266 }
1267 if (!ok)
1268 token.type_ = tokenError;
1269 token.end_ = current_;
1270 return ok;
1271}
1272
1273void OurReader::skipSpaces() {
1274 while (current_ != end_) {
1275 Char c = *current_;
1276 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1277 ++current_;
1278 else
1279 break;
1280 }
1281}
1282
1283// Skip whitespace and any comments, leaving current_ at the next significant
1284// character. Consumed comments are recorded (commentsBefore_) so the next value
1285// still receives them; if none follows they are simply not attached. This lets
1286// callers peek for a delimiter that is preceded by comments (e.g. a ']' after a
1287// trailing comma -- see readArray and issue #1500).
1288void OurReader::skipCommentTokens() {
1289 skipSpaces();
1290 if (!features_.allowComments_)
1291 return;
1292 while (current_ != end_ && *current_ == '/' && (current_ + 1) != end_ &&
1293 (current_[1] == '/' || current_[1] == '*')) {
1294 Token comment;
1295 if (!readToken(comment))
1296 return;
1297 skipSpaces();
1298 }
1299}
1300
1301void OurReader::skipBom(bool skipBom) {
1302 // The default behavior is to skip BOM.
1303 if (skipBom) {
1304 if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
1305 begin_ += 3;
1306 current_ = begin_;
1307 }
1308 }
1309}
1310
1311bool OurReader::match(const Char* pattern, int patternLength) {
1312 if (end_ - current_ < patternLength)
1313 return false;
1314 int index = patternLength;
1315 while (index--)
1316 if (current_[index] != pattern[index])
1317 return false;
1318 current_ += patternLength;
1319 return true;
1320}
1321
1322bool OurReader::readComment() {
1323 const Location commentBegin = current_ - 1;
1324 const Char c = getNextChar();
1325 bool successful = false;
1326 bool cStyleWithEmbeddedNewline = false;
1327
1328 const bool isCStyleComment = (c == '*');
1329 const bool isCppStyleComment = (c == '/');
1330 if (isCStyleComment) {
1331 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1332 } else if (isCppStyleComment) {
1333 successful = readCppStyleComment();
1334 }
1335
1336 if (!successful)
1337 return false;
1338
1339 if (collectComments_) {
1340 CommentPlacement placement = commentBefore;
1341
1342 if (!lastValueHasAComment_) {
1343 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1344 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1345 placement = commentAfterOnSameLine;
1346 }
1347 }
1348 // The gap between the last value and this comment only grows as more
1349 // comments are consumed, so a later comment can never be on the same
1350 // line as that value. Mark it handled to avoid re-scanning the same
1351 // growing prefix for every following comment (quadratic behavior).
1352 lastValueHasAComment_ = true;
1353 }
1354
1355 addComment(commentBegin, current_, placement);
1356 }
1357 return true;
1358}
1359
1360String OurReader::normalizeEOL(OurReader::Location begin,
1361 OurReader::Location end) {
1362 String normalized;
1363 normalized.reserve(static_cast<size_t>(end - begin));
1364 OurReader::Location current = begin;
1365 while (current != end) {
1366 char c = *current++;
1367 if (c == '\r') {
1368 if (current != end && *current == '\n')
1369 // convert dos EOL
1370 ++current;
1371 // convert Mac EOL
1372 normalized += '\n';
1373 } else {
1374 normalized += c;
1375 }
1376 }
1377 return normalized;
1378}
1379
1380void OurReader::addComment(Location begin, Location end,
1381 CommentPlacement placement) {
1382 assert(collectComments_);
1383 const String& normalized = normalizeEOL(begin, end);
1384 if (placement == commentAfterOnSameLine) {
1385 assert(lastValue_ != nullptr);
1386 lastValue_->setComment(normalized, placement);
1387 } else {
1388 commentsBefore_ += normalized;
1389 }
1390}
1391
1392bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1393 *containsNewLineResult = false;
1394
1395 while ((current_ + 1) < end_) {
1396 Char c = getNextChar();
1397 if (c == '*' && *current_ == '/')
1398 break;
1399 if (c == '\n')
1400 *containsNewLineResult = true;
1401 }
1402
1403 return getNextChar() == '/';
1404}
1405
1406bool OurReader::readCppStyleComment() {
1407 while (current_ != end_) {
1408 Char c = getNextChar();
1409 if (c == '\n')
1410 break;
1411 if (c == '\r') {
1412 // Consume DOS EOL. It will be normalized in addComment.
1413 if (current_ != end_ && *current_ == '\n')
1414 getNextChar();
1415 // Break on Moc OS 9 EOL.
1416 break;
1417 }
1418 }
1419 return true;
1420}
1421
1422bool OurReader::readNumber(bool checkInf) {
1423 Location p = current_;
1424 if (checkInf && p != end_ && *p == 'I') {
1425 current_ = ++p;
1426 return false;
1427 }
1428 char c = '0'; // stopgap for already consumed character
1429 // integral part
1430 while (c >= '0' && c <= '9')
1431 c = (current_ = p) < end_ ? *p++ : '\0';
1432 // fractional part
1433 if (c == '.') {
1434 c = (current_ = p) < end_ ? *p++ : '\0';
1435 while (c >= '0' && c <= '9')
1436 c = (current_ = p) < end_ ? *p++ : '\0';
1437 }
1438 // exponential part
1439 if (c == 'e' || c == 'E') {
1440 c = (current_ = p) < end_ ? *p++ : '\0';
1441 if (c == '+' || c == '-')
1442 c = (current_ = p) < end_ ? *p++ : '\0';
1443 while (c >= '0' && c <= '9')
1444 c = (current_ = p) < end_ ? *p++ : '\0';
1445 }
1446 return true;
1447}
1448bool OurReader::readString() {
1449 Char c = 0;
1450 while (current_ != end_) {
1451 c = getNextChar();
1452 if (c == '\\')
1453 getNextChar();
1454 else if (c == '"')
1455 break;
1456 }
1457 return c == '"';
1458}
1459
1460bool OurReader::readStringSingleQuote() {
1461 Char c = 0;
1462 while (current_ != end_) {
1463 c = getNextChar();
1464 if (c == '\\')
1465 getNextChar();
1466 else if (c == '\'')
1467 break;
1468 }
1469 return c == '\'';
1470}
1471
1472bool OurReader::readObject(Token& token) {
1473 Token tokenName;
1474 String name;
1475 Value init(objectValue);
1476 currentValue().swapPayload(init);
1477 currentValue().setOffsetStart(token.start_ - begin_);
1478 while (readTokenSkippingComments(tokenName)) {
1479 if (tokenName.type_ == tokenObjectEnd &&
1480 (name.empty() ||
1481 features_.allowTrailingCommas_)) // empty object or trailing comma
1482 return true;
1483 name.clear();
1484 if (tokenName.type_ == tokenString) {
1485 if (!decodeString(tokenName, name))
1486 return recoverFromError(tokenObjectEnd);
1487 } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1488 Value numberName;
1489 if (!decodeNumber(tokenName, numberName))
1490 return recoverFromError(tokenObjectEnd);
1491 name = numberName.asString();
1492 } else {
1493 break;
1494 }
1495 if (name.length() >= (1U << 30))
1496 throwRuntimeError("keylength >= 2^30");
1497 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1498 String msg = "Duplicate key: '" + name + "'";
1499 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1500 }
1501
1502 Token colon;
1503 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1504 return addErrorAndRecover("Missing ':' after object member name", colon,
1505 tokenObjectEnd);
1506 }
1507 Value& value = currentValue()[name];
1508 nodes_.push(&value);
1509 bool ok = readValue();
1510 nodes_.pop();
1511 if (!ok) // error already set
1512 return recoverFromError(tokenObjectEnd);
1513
1514 Token comma;
1515 if (!readTokenSkippingComments(comma) ||
1516 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
1517 return addErrorAndRecover("Missing ',' or '}' in object declaration",
1518 comma, tokenObjectEnd);
1519 }
1520 if (comma.type_ == tokenObjectEnd)
1521 return true;
1522 }
1523 return addErrorAndRecover("Missing '}' or object member name", tokenName,
1524 tokenObjectEnd);
1525}
1526
1527bool OurReader::readArray(Token& token) {
1528 Value init(arrayValue);
1529 currentValue().swapPayload(init);
1530 currentValue().setOffsetStart(token.start_ - begin_);
1531 int index = 0;
1532 for (;;) {
1533 // Skip comments too, so a ']' that follows a trailing comma (or comments in
1534 // an otherwise empty array) is recognized rather than mistaken for the
1535 // start of another value. See issue #1500.
1536 skipCommentTokens();
1537 if (current_ != end_ && *current_ == ']' &&
1538 (index == 0 ||
1539 (features_.allowTrailingCommas_ &&
1540 !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
1541 // comma
1542 {
1543 Token endArray;
1544 readToken(endArray);
1545 return true;
1546 }
1547 Value& value = currentValue()[index++];
1548 nodes_.push(&value);
1549 bool ok = readValue();
1550 nodes_.pop();
1551 if (!ok) // error already set
1552 return recoverFromError(tokenArrayEnd);
1553
1554 Token currentToken;
1555 // Accept Comment after last item in the array.
1556 ok = readTokenSkippingComments(currentToken);
1557 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1558 currentToken.type_ != tokenArrayEnd);
1559 if (!ok || badTokenType) {
1560 return addErrorAndRecover("Missing ',' or ']' in array declaration",
1561 currentToken, tokenArrayEnd);
1562 }
1563 if (currentToken.type_ == tokenArrayEnd)
1564 break;
1565 }
1566 return true;
1567}
1568
1569bool OurReader::decodeNumber(Token& token) {
1570 Value decoded;
1571 if (!decodeNumber(token, decoded))
1572 return false;
1573 currentValue().swapPayload(decoded);
1574 currentValue().setOffsetStart(token.start_ - begin_);
1575 currentValue().setOffsetLimit(token.end_ - begin_);
1576 return true;
1577}
1578
1579bool OurReader::decodeNumber(Token& token, Value& decoded) {
1580 // Attempts to parse the number as an integer. If the number is
1581 // larger than the maximum supported value of an integer then
1582 // we decode the number as a double.
1583 Location current = token.start_;
1584 const bool isNegative = *current == '-';
1585 if (isNegative) {
1586 ++current;
1587 }
1588
1589 // We assume we can represent the largest and smallest integer types as
1590 // unsigned integers with separate sign. This is only true if they can fit
1591 // into an unsigned integer.
1592 static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1593 "Int must be smaller than UInt");
1594
1595 // We need to convert minLargestInt into a positive number. The easiest way
1596 // to do this conversion is to assume our "threshold" value of minLargestInt
1597 // divided by 10 can fit in maxLargestInt when absolute valued. This should
1598 // be a safe assumption.
1599 static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1600 "The absolute value of minLargestInt must be greater than or "
1601 "equal to maxLargestInt");
1602 static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1603 "The absolute value of minLargestInt must be only 1 magnitude "
1604 "larger than maxLargest Int");
1605
1606 static constexpr Value::LargestUInt positive_threshold =
1607 Value::maxLargestUInt / 10;
1608 static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1609
1610 // For the negative values, we have to be more careful. Since typically
1611 // -Value::minLargestInt will cause an overflow, we first divide by 10 and
1612 // then take the inverse. This assumes that minLargestInt is only a single
1613 // power of 10 different in magnitude, which we check above. For the last
1614 // digit, we take the modulus before negating for the same reason.
1615 static constexpr auto negative_threshold =
1616 Value::LargestUInt(-(Value::minLargestInt / 10));
1617 static constexpr auto negative_last_digit =
1618 Value::UInt(-(Value::minLargestInt % 10));
1619
1620 const Value::LargestUInt threshold =
1621 isNegative ? negative_threshold : positive_threshold;
1622 const Value::UInt max_last_digit =
1623 isNegative ? negative_last_digit : positive_last_digit;
1624
1625 Value::LargestUInt value = 0;
1626 while (current < token.end_) {
1627 Char c = *current++;
1628 if (c < '0' || c > '9')
1629 return decodeDouble(token, decoded);
1630
1631 const auto digit(static_cast<Value::UInt>(c - '0'));
1632 if (value >= threshold) {
1633 // We've hit or exceeded the max value divided by 10 (rounded down). If
1634 // a) we've only just touched the limit, meaning value == threshold,
1635 // b) this is the last digit, or
1636 // c) it's small enough to fit in that rounding delta, we're okay.
1637 // Otherwise treat this number as a double to avoid overflow.
1638 if (value > threshold || current != token.end_ ||
1639 digit > max_last_digit) {
1640 return decodeDouble(token, decoded);
1641 }
1642 }
1643 value = value * 10 + digit;
1644 }
1645
1646 if (isNegative) {
1647 // We use the same magnitude assumption here, just in case.
1648 const auto last_digit = static_cast<Value::UInt>(value % 10);
1649 decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
1650 } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
1651 decoded = Value::LargestInt(value);
1652 } else {
1653 decoded = value;
1654 }
1655
1656 return true;
1657}
1658
1659bool OurReader::decodeDouble(Token& token) {
1660 Value decoded;
1661 if (!decodeDouble(token, decoded))
1662 return false;
1663 currentValue().swapPayload(decoded);
1664 currentValue().setOffsetStart(token.start_ - begin_);
1665 currentValue().setOffsetLimit(token.end_ - begin_);
1666 return true;
1667}
1668
1669bool OurReader::decodeDouble(Token& token, Value& decoded) {
1670 double value = 0;
1671 IStringStream is(String(token.start_, token.end_));
1672 is.imbue(std::locale::classic());
1673 if (!(is >> value)) {
1674 if (value == std::numeric_limits<double>::max())
1675 value = std::numeric_limits<double>::infinity();
1676 else if (value == std::numeric_limits<double>::lowest())
1677 value = -std::numeric_limits<double>::infinity();
1678 // operator>> sets failbit for a subnormal result (underflow) even though
1679 // it produced the correctly-rounded value, which made such numbers fail to
1680 // parse back after jsoncpp serialized them. Keep a subnormal value instead
1681 // of rejecting it. See issue #1427. Other failures -- malformed numbers
1682 // like "0e" or "0e+", or non-numbers -- leave the value at zero/non-finite
1683 // and are still rejected.
1684 else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
1685 return addError(
1686 "'" + String(token.start_, token.end_) + "' is not a number.", token);
1687 }
1688 decoded = value;
1689 return true;
1690}
1691
1692bool OurReader::decodeString(Token& token) {
1693 String decoded_string;
1694 if (!decodeString(token, decoded_string))
1695 return false;
1696 Value decoded(decoded_string);
1697 currentValue().swapPayload(decoded);
1698 currentValue().setOffsetStart(token.start_ - begin_);
1699 currentValue().setOffsetLimit(token.end_ - begin_);
1700 return true;
1701}
1702
1703bool OurReader::decodeString(Token& token, String& decoded) {
1704 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
1705 Location current = token.start_ + 1; // skip '"'
1706 Location end = token.end_ - 1; // do not include '"'
1707 while (current != end) {
1708 Char c = *current++;
1709 if (c == '"')
1710 break;
1711 if (c == '\\') {
1712 if (current == end)
1713 return addError("Empty escape sequence in string", token, current);
1714 Char escape = *current++;
1715 switch (escape) {
1716 case '"':
1717 decoded += '"';
1718 break;
1719 case '/':
1720 decoded += '/';
1721 break;
1722 case '\\':
1723 decoded += '\\';
1724 break;
1725 case 'b':
1726 decoded += '\b';
1727 break;
1728 case 'f':
1729 decoded += '\f';
1730 break;
1731 case 'n':
1732 decoded += '\n';
1733 break;
1734 case 'r':
1735 decoded += '\r';
1736 break;
1737 case 't':
1738 decoded += '\t';
1739 break;
1740 case 'u': {
1741 unsigned int unicode;
1742 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1743 return false;
1744 decoded += codePointToUTF8(unicode);
1745 } break;
1746 default:
1747 return addError("Bad escape sequence in string", token, current);
1748 }
1749 } else {
1750 if (static_cast<unsigned char>(c) < 0x20)
1751 return addError("Control character in string", token, current - 1);
1752 decoded += c;
1753 }
1754 }
1755 return true;
1756}
1757
1758bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1759 Location end, unsigned int& unicode) {
1760
1761 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1762 return false;
1763 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1764 // surrogate pairs
1765 if (end - current < 6)
1766 return addError(
1767 "additional six characters expected to parse unicode surrogate pair.",
1768 token, current);
1769 if (*(current++) == '\\' && *(current++) == 'u') {
1770 unsigned int surrogatePair;
1771 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1772 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1773 } else
1774 return false;
1775 } else
1776 return addError("expecting another \\u token to begin the second half of "
1777 "a unicode surrogate pair",
1778 token, current);
1779 }
1780 return true;
1781}
1782
1783bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1784 Location end,
1785 unsigned int& ret_unicode) {
1786 if (end - current < 4)
1787 return addError(
1788 "Bad unicode escape sequence in string: four digits expected.", token,
1789 current);
1790 int unicode = 0;
1791 for (int index = 0; index < 4; ++index) {
1792 Char c = *current++;
1793 unicode *= 16;
1794 if (c >= '0' && c <= '9')
1795 unicode += c - '0';
1796 else if (c >= 'a' && c <= 'f')
1797 unicode += c - 'a' + 10;
1798 else if (c >= 'A' && c <= 'F')
1799 unicode += c - 'A' + 10;
1800 else
1801 return addError(
1802 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1803 token, current);
1804 }
1805 ret_unicode = static_cast<unsigned int>(unicode);
1806 return true;
1807}
1808
1809bool OurReader::addError(const String& message, Token& token, Location extra) {
1810 ErrorInfo info;
1811 info.token_ = token;
1812 info.message_ = message;
1813 info.extra_ = extra;
1814 errors_.push_back(info);
1815 return false;
1816}
1817
1818bool OurReader::recoverFromError(TokenType skipUntilToken) {
1819 size_t errorCount = errors_.size();
1820 Token skip;
1821 for (;;) {
1822 if (!readToken(skip))
1823 errors_.resize(errorCount); // discard errors caused by recovery
1824 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1825 break;
1826 }
1827 errors_.resize(errorCount);
1828 return false;
1829}
1830
1831bool OurReader::addErrorAndRecover(const String& message, Token& token,
1832 TokenType skipUntilToken) {
1833 addError(message, token);
1834 return recoverFromError(skipUntilToken);
1835}
1836
1837Value& OurReader::currentValue() { return *(nodes_.top()); }
1838
1839OurReader::Char OurReader::getNextChar() {
1840 if (current_ == end_)
1841 return 0;
1842 return *current_++;
1843}
1844
1845void OurReader::getLocationLineAndColumn(Location location, int& line,
1846 int& column) const {
1847 Location current = begin_;
1848 Location lastLineStart = current;
1849 line = 0;
1850 while (current < location && current != end_) {
1851 Char c = *current++;
1852 if (c == '\r') {
1853 if (current != end_ && *current == '\n')
1854 ++current;
1855 lastLineStart = current;
1856 ++line;
1857 } else if (c == '\n') {
1858 lastLineStart = current;
1859 ++line;
1860 }
1861 }
1862 // column & line start at 1
1863 column = int(location - lastLineStart) + 1;
1864 ++line;
1865}
1866
1867String OurReader::getLocationLineAndColumn(Location location) const {
1868 int line, column;
1869 getLocationLineAndColumn(location, line, column);
1870 char buffer[18 + 16 + 16 + 1];
1871 jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1872 return buffer;
1873}
1874
1875String OurReader::getFormattedErrorMessages() const {
1876 String formattedMessage;
1877 for (const auto& error : errors_) {
1878 formattedMessage +=
1879 "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
1880 formattedMessage += " " + error.message_ + "\n";
1881 if (error.extra_)
1882 formattedMessage +=
1883 "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
1884 }
1885 return formattedMessage;
1886}
1887
1888std::vector<CharReader::StructuredError>
1889OurReader::getStructuredErrors() const {
1890 std::vector<CharReader::StructuredError> allErrors;
1891 for (const auto& error : errors_) {
1892 CharReader::StructuredError structured;
1893 structured.offset_start = error.token_.start_ - begin_;
1894 structured.offset_limit = error.token_.end_ - begin_;
1895 structured.message = error.message_;
1896 allErrors.push_back(structured);
1897 }
1898 return allErrors;
1899}
1900
1901class OurCharReader : public CharReader {
1902
1903public:
1904 OurCharReader(bool collectComments, OurFeatures const& features)
1905 : CharReader(
1906 std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1907
1908protected:
1909 class OurImpl : public Impl {
1910 public:
1911 OurImpl(bool collectComments, OurFeatures const& features)
1912 : collectComments_(collectComments), reader_(features) {}
1913
1914 bool parse(char const* beginDoc, char const* endDoc, Value* root,
1915 String* errs) override {
1916 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1917 if (errs) {
1918 *errs = reader_.getFormattedErrorMessages();
1919 }
1920 return ok;
1921 }
1922
1923 std::vector<CharReader::StructuredError>
1924 getStructuredErrors() const override {
1925 return reader_.getStructuredErrors();
1926 }
1927
1928 private:
1929 bool const collectComments_;
1930 OurReader reader_;
1931 };
1932};
1933
1937 bool collectComments = settings_["collectComments"].asBool();
1938 OurFeatures features = OurFeatures::all();
1939 features.allowComments_ = settings_["allowComments"].asBool();
1940 features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
1941 features.strictRoot_ = settings_["strictRoot"].asBool();
1942 features.allowDroppedNullPlaceholders_ =
1943 settings_["allowDroppedNullPlaceholders"].asBool();
1944 features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
1945 features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
1946
1947 // Stack limit is always a size_t, so we get this as an unsigned int
1948 // regardless of it we have 64-bit integer support enabled.
1949 features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
1950 features.failIfExtra_ = settings_["failIfExtra"].asBool();
1951 features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
1952 features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
1953 features.skipBom_ = settings_["skipBom"].asBool();
1954 return new OurCharReader(collectComments, features);
1955}
1956
1958 static const auto& valid_keys = *new std::set<String>{
1959 "collectComments",
1960 "allowComments",
1961 "allowTrailingCommas",
1962 "strictRoot",
1963 "allowDroppedNullPlaceholders",
1964 "allowNumericKeys",
1965 "allowSingleQuotes",
1966 "stackLimit",
1967 "failIfExtra",
1968 "rejectDupKeys",
1969 "allowSpecialFloats",
1970 "skipBom",
1971 };
1972 for (auto si = settings_.begin(); si != settings_.end(); ++si) {
1973 auto key = si.name();
1974 if (valid_keys.count(key))
1975 continue;
1976 if (invalid)
1977 (*invalid)[key] = *si;
1978 else
1979 return false;
1980 }
1981 return invalid ? invalid->empty() : true;
1982}
1983
1985 return settings_[key];
1986}
1987// static
1990 (*settings)["allowComments"] = false;
1991 (*settings)["allowTrailingCommas"] = false;
1992 (*settings)["strictRoot"] = true;
1993 (*settings)["allowDroppedNullPlaceholders"] = false;
1994 (*settings)["allowNumericKeys"] = false;
1995 (*settings)["allowSingleQuotes"] = false;
1996 (*settings)["stackLimit"] = 256;
1997 (*settings)["failIfExtra"] = true;
1998 (*settings)["rejectDupKeys"] = true;
1999 (*settings)["allowSpecialFloats"] = false;
2000 (*settings)["skipBom"] = true;
2002}
2003// static
2006 (*settings)["collectComments"] = true;
2007 (*settings)["allowComments"] = true;
2008 (*settings)["allowTrailingCommas"] = true;
2009 (*settings)["strictRoot"] = false;
2010 (*settings)["allowDroppedNullPlaceholders"] = false;
2011 (*settings)["allowNumericKeys"] = false;
2012 (*settings)["allowSingleQuotes"] = false;
2013 (*settings)["stackLimit"] = 256;
2014 (*settings)["failIfExtra"] = false;
2015 (*settings)["rejectDupKeys"] = false;
2016 (*settings)["allowSpecialFloats"] = false;
2017 (*settings)["skipBom"] = true;
2019}
2020// static
2023 (*settings)["allowComments"] = false;
2024 (*settings)["allowTrailingCommas"] = false;
2025 (*settings)["strictRoot"] = false;
2026 (*settings)["allowDroppedNullPlaceholders"] = false;
2027 (*settings)["allowNumericKeys"] = false;
2028 (*settings)["allowSingleQuotes"] = false;
2029 (*settings)["stackLimit"] = 256;
2030 (*settings)["failIfExtra"] = true;
2031 (*settings)["rejectDupKeys"] = false;
2032 (*settings)["allowSpecialFloats"] = false;
2033 (*settings)["skipBom"] = false;
2035}
2036
2037std::vector<CharReader::StructuredError>
2039 return _impl->getStructuredErrors();
2040}
2041
2042bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
2043 String* errs) {
2044 return _impl->parse(beginDoc, endDoc, root, errs);
2045}
2046
2048// global functions
2049
2050bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
2051 String* errs) {
2052 OStringStream ssin;
2053 ssin << sin.rdbuf();
2054 String doc = std::move(ssin).str();
2055 char const* begin = doc.data();
2056 char const* end = begin + doc.size();
2057 // Note that we do not actually need a null-terminator.
2058 CharReaderPtr const reader(fact.newCharReader());
2059 return reader->parse(begin, end, root, errs);
2060}
2061
2064 String errs;
2065 bool ok = parseFromStream(b, sin, &root, &errs);
2066 if (!ok) {
2067 throwRuntimeError(errs);
2068 }
2069 return sin;
2070}
2071
2072} // 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