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
7 #if !defined(JSON_IS_AMALGAMATION)
9 #include <json/assertions.h>
10 #include <json/reader.h>
11 #include <json/value.h>
12 #endif // if !defined(JSON_IS_AMALGAMATION)
23 #if __cplusplus >= 201103L
26 #define sscanf std::sscanf
32 #if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
33 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
34 #endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
38 // Disable warning about strdup being deprecated.
39 #pragma warning(disable : 4996)
42 // Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
43 // time to change the stack limit
44 #if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
45 #define JSONCPP_DEPRECATED_STACK_LIMIT 1000
48 static size_t const stackLimit_g =
49 JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
53 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
54 typedef std::unique_ptr<CharReader> CharReaderPtr;
56 typedef std::auto_ptr<CharReader> CharReaderPtr;
59 // Implementation of class Features
60 // ////////////////////////////////
62 Features::Features() = default;
64 Features Features::all() { return {}; }
66 Features Features::strictMode() {
68 features.allowComments_ = false;
69 features.strictRoot_ = true;
70 features.allowDroppedNullPlaceholders_ = false;
71 features.allowNumericKeys_ = false;
75 // Implementation of class Reader
76 // ////////////////////////////////
78 bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
79 for (; begin < end; ++begin)
80 if (*begin == '\n' || *begin == '\r')
86 // //////////////////////////////////////////////////////////////////
89 : errors_(), document_(), commentsBefore_(), features_(Features::all()) {}
91 Reader::Reader(const Features& features)
92 : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
93 lastValue_(), commentsBefore_(), features_(features), collectComments_() {
96 bool Reader::parse(const std::string& document,
98 bool collectComments) {
99 document_.assign(document.begin(), document.end());
100 const char* begin = document_.c_str();
101 const char* end = begin + document_.length();
102 return parse(begin, end, root, collectComments);
105 bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
106 // std::istream_iterator<char> begin(is);
107 // std::istream_iterator<char> end;
108 // Those would allow streamed input from a file, if parse() were a
109 // template function.
111 // Since String is reference-counted, this at least does not
112 // create an extra copy.
114 std::getline(is, doc, (char)EOF);
115 return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
118 bool Reader::parse(const char* beginDoc,
121 bool collectComments) {
122 if (!features_.allowComments_) {
123 collectComments = false;
128 collectComments_ = collectComments;
130 lastValueEnd_ = nullptr;
131 lastValue_ = nullptr;
132 commentsBefore_.clear();
134 while (!nodes_.empty())
138 bool successful = readValue();
140 skipCommentTokens(token);
141 if (collectComments_ && !commentsBefore_.empty())
142 root.setComment(commentsBefore_, commentAfter);
143 if (features_.strictRoot_) {
144 if (!root.isArray() && !root.isObject()) {
145 // Set error location to start of doc, ideally should be first token found
147 token.type_ = tokenError;
148 token.start_ = beginDoc;
151 "A valid JSON document must be either an array or an object value.",
159 bool Reader::readValue() {
160 // readValue() may call itself only if it calls readObject() or ReadArray().
161 // These methods execute nodes_.push() just before and nodes_.pop)() just
162 // after calling readValue(). parse() executes one nodes_.push(), so > instead
164 if (nodes_.size() > stackLimit_g)
165 throwRuntimeError("Exceeded stackLimit in readValue().");
168 skipCommentTokens(token);
169 bool successful = true;
171 if (collectComments_ && !commentsBefore_.empty()) {
172 currentValue().setComment(commentsBefore_, commentBefore);
173 commentsBefore_.clear();
176 switch (token.type_) {
177 case tokenObjectBegin:
178 successful = readObject(token);
179 currentValue().setOffsetLimit(current_ - begin_);
181 case tokenArrayBegin:
182 successful = readArray(token);
183 currentValue().setOffsetLimit(current_ - begin_);
186 successful = decodeNumber(token);
189 successful = decodeString(token);
193 currentValue().swapPayload(v);
194 currentValue().setOffsetStart(token.start_ - begin_);
195 currentValue().setOffsetLimit(token.end_ - begin_);
199 currentValue().swapPayload(v);
200 currentValue().setOffsetStart(token.start_ - begin_);
201 currentValue().setOffsetLimit(token.end_ - begin_);
205 currentValue().swapPayload(v);
206 currentValue().setOffsetStart(token.start_ - begin_);
207 currentValue().setOffsetLimit(token.end_ - begin_);
209 case tokenArraySeparator:
212 if (features_.allowDroppedNullPlaceholders_) {
213 // "Un-read" the current token and mark the current value as a null
217 currentValue().swapPayload(v);
218 currentValue().setOffsetStart(current_ - begin_ - 1);
219 currentValue().setOffsetLimit(current_ - begin_);
221 } // Else, fall through...
223 currentValue().setOffsetStart(token.start_ - begin_);
224 currentValue().setOffsetLimit(token.end_ - begin_);
225 return addError("Syntax error: value, object or array expected.", token);
228 if (collectComments_) {
229 lastValueEnd_ = current_;
230 lastValue_ = ¤tValue();
236 void Reader::skipCommentTokens(Token& token) {
237 if (features_.allowComments_) {
240 } while (token.type_ == tokenComment);
246 bool Reader::readToken(Token& token) {
248 token.start_ = current_;
249 Char c = getNextChar();
253 token.type_ = tokenObjectBegin;
256 token.type_ = tokenObjectEnd;
259 token.type_ = tokenArrayBegin;
262 token.type_ = tokenArrayEnd;
265 token.type_ = tokenString;
269 token.type_ = tokenComment;
283 token.type_ = tokenNumber;
287 token.type_ = tokenTrue;
288 ok = match("rue", 3);
291 token.type_ = tokenFalse;
292 ok = match("alse", 4);
295 token.type_ = tokenNull;
296 ok = match("ull", 3);
299 token.type_ = tokenArraySeparator;
302 token.type_ = tokenMemberSeparator;
305 token.type_ = tokenEndOfStream;
312 token.type_ = tokenError;
313 token.end_ = current_;
317 void Reader::skipSpaces() {
318 while (current_ != end_) {
320 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
327 bool Reader::match(Location pattern, int patternLength) {
328 if (end_ - current_ < patternLength)
330 int index = patternLength;
332 if (current_[index] != pattern[index])
334 current_ += patternLength;
338 bool Reader::readComment() {
339 Location commentBegin = current_ - 1;
340 Char c = getNextChar();
341 bool successful = false;
343 successful = readCStyleComment();
345 successful = readCppStyleComment();
349 if (collectComments_) {
350 CommentPlacement placement = commentBefore;
351 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
352 if (c != '*' || !containsNewLine(commentBegin, current_))
353 placement = commentAfterOnSameLine;
356 addComment(commentBegin, current_, placement);
361 String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
363 normalized.reserve(static_cast<size_t>(end - begin));
364 Reader::Location current = begin;
365 while (current != end) {
368 if (current != end && *current == '\n')
380 void Reader::addComment(Location begin,
382 CommentPlacement placement) {
383 assert(collectComments_);
384 const String& normalized = normalizeEOL(begin, end);
385 if (placement == commentAfterOnSameLine) {
386 assert(lastValue_ != nullptr);
387 lastValue_->setComment(normalized, placement);
389 commentsBefore_ += normalized;
393 bool Reader::readCStyleComment() {
394 while ((current_ + 1) < end_) {
395 Char c = getNextChar();
396 if (c == '*' && *current_ == '/')
399 return getNextChar() == '/';
402 bool Reader::readCppStyleComment() {
403 while (current_ != end_) {
404 Char c = getNextChar();
408 // Consume DOS EOL. It will be normalized in addComment.
409 if (current_ != end_ && *current_ == '\n')
411 // Break on Moc OS 9 EOL.
418 void Reader::readNumber() {
419 const char* p = current_;
420 char c = '0'; // stopgap for already consumed character
422 while (c >= '0' && c <= '9')
423 c = (current_ = p) < end_ ? *p++ : '\0';
426 c = (current_ = p) < end_ ? *p++ : '\0';
427 while (c >= '0' && c <= '9')
428 c = (current_ = p) < end_ ? *p++ : '\0';
431 if (c == 'e' || c == 'E') {
432 c = (current_ = p) < end_ ? *p++ : '\0';
433 if (c == '+' || c == '-')
434 c = (current_ = p) < end_ ? *p++ : '\0';
435 while (c >= '0' && c <= '9')
436 c = (current_ = p) < end_ ? *p++ : '\0';
440 bool Reader::readString() {
442 while (current_ != end_) {
452 bool Reader::readObject(Token& token) {
455 Value init(objectValue);
456 currentValue().swapPayload(init);
457 currentValue().setOffsetStart(token.start_ - begin_);
458 while (readToken(tokenName)) {
459 bool initialTokenOk = true;
460 while (tokenName.type_ == tokenComment && initialTokenOk)
461 initialTokenOk = readToken(tokenName);
464 if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
467 if (tokenName.type_ == tokenString) {
468 if (!decodeString(tokenName, name))
469 return recoverFromError(tokenObjectEnd);
470 } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
472 if (!decodeNumber(tokenName, numberName))
473 return recoverFromError(tokenObjectEnd);
474 name = String(numberName.asCString());
480 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
481 return addErrorAndRecover("Missing ':' after object member name", colon,
484 Value& value = currentValue()[name];
486 bool ok = readValue();
488 if (!ok) // error already set
489 return recoverFromError(tokenObjectEnd);
492 if (!readToken(comma) ||
493 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
494 comma.type_ != tokenComment)) {
495 return addErrorAndRecover("Missing ',' or '}' in object declaration",
496 comma, tokenObjectEnd);
498 bool finalizeTokenOk = true;
499 while (comma.type_ == tokenComment && finalizeTokenOk)
500 finalizeTokenOk = readToken(comma);
501 if (comma.type_ == tokenObjectEnd)
504 return addErrorAndRecover("Missing '}' or object member name", tokenName,
508 bool Reader::readArray(Token& token) {
509 Value init(arrayValue);
510 currentValue().swapPayload(init);
511 currentValue().setOffsetStart(token.start_ - begin_);
513 if (current_ != end_ && *current_ == ']') // empty array
521 Value& value = currentValue()[index++];
523 bool ok = readValue();
525 if (!ok) // error already set
526 return recoverFromError(tokenArrayEnd);
529 // Accept Comment after last item in the array.
530 ok = readToken(currentToken);
531 while (currentToken.type_ == tokenComment && ok) {
532 ok = readToken(currentToken);
534 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
535 currentToken.type_ != tokenArrayEnd);
536 if (!ok || badTokenType) {
537 return addErrorAndRecover("Missing ',' or ']' in array declaration",
538 currentToken, tokenArrayEnd);
540 if (currentToken.type_ == tokenArrayEnd)
546 bool Reader::decodeNumber(Token& token) {
548 if (!decodeNumber(token, decoded))
550 currentValue().swapPayload(decoded);
551 currentValue().setOffsetStart(token.start_ - begin_);
552 currentValue().setOffsetLimit(token.end_ - begin_);
556 bool Reader::decodeNumber(Token& token, Value& decoded) {
557 // Attempts to parse the number as an integer. If the number is
558 // larger than the maximum supported value of an integer then
559 // we decode the number as a double.
560 Location current = token.start_;
561 bool isNegative = *current == '-';
564 // TODO: Help the compiler do the div and mod at compile time or get rid of
566 Value::LargestUInt maxIntegerValue =
567 isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
568 : Value::maxLargestUInt;
569 Value::LargestUInt threshold = maxIntegerValue / 10;
570 Value::LargestUInt value = 0;
571 while (current < token.end_) {
573 if (c < '0' || c > '9')
574 return decodeDouble(token, decoded);
575 auto digit(static_cast<Value::UInt>(c - '0'));
576 if (value >= threshold) {
577 // We've hit or exceeded the max value divided by 10 (rounded down). If
578 // a) we've only just touched the limit, b) this is the last digit, and
579 // c) it's small enough to fit in that rounding delta, we're okay.
580 // Otherwise treat this number as a double to avoid overflow.
581 if (value > threshold || current != token.end_ ||
582 digit > maxIntegerValue % 10) {
583 return decodeDouble(token, decoded);
586 value = value * 10 + digit;
588 if (isNegative && value == maxIntegerValue)
589 decoded = Value::minLargestInt;
591 decoded = -Value::LargestInt(value);
592 else if (value <= Value::LargestUInt(Value::maxInt))
593 decoded = Value::LargestInt(value);
599 bool Reader::decodeDouble(Token& token) {
601 if (!decodeDouble(token, decoded))
603 currentValue().swapPayload(decoded);
604 currentValue().setOffsetStart(token.start_ - begin_);
605 currentValue().setOffsetLimit(token.end_ - begin_);
609 bool Reader::decodeDouble(Token& token, Value& decoded) {
611 String buffer(token.start_, token.end_);
612 IStringStream is(buffer);
615 "'" + String(token.start_, token.end_) + "' is not a number.", token);
620 bool Reader::decodeString(Token& token) {
621 String decoded_string;
622 if (!decodeString(token, decoded_string))
624 Value decoded(decoded_string);
625 currentValue().swapPayload(decoded);
626 currentValue().setOffsetStart(token.start_ - begin_);
627 currentValue().setOffsetLimit(token.end_ - begin_);
631 bool Reader::decodeString(Token& token, String& decoded) {
632 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
633 Location current = token.start_ + 1; // skip '"'
634 Location end = token.end_ - 1; // do not include '"'
635 while (current != end) {
639 else if (c == '\\') {
641 return addError("Empty escape sequence in string", token, current);
642 Char escape = *current++;
669 unsigned int unicode;
670 if (!decodeUnicodeCodePoint(token, current, end, unicode))
672 decoded += codePointToUTF8(unicode);
675 return addError("Bad escape sequence in string", token, current);
684 bool Reader::decodeUnicodeCodePoint(Token& token,
687 unsigned int& unicode) {
689 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
691 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
693 if (end - current < 6)
695 "additional six characters expected to parse unicode surrogate pair.",
697 if (*(current++) == '\\' && *(current++) == 'u') {
698 unsigned int surrogatePair;
699 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
700 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
704 return addError("expecting another \\u token to begin the second half of "
705 "a unicode surrogate pair",
711 bool Reader::decodeUnicodeEscapeSequence(Token& token,
714 unsigned int& ret_unicode) {
715 if (end - current < 4)
717 "Bad unicode escape sequence in string: four digits expected.", token,
720 for (int index = 0; index < 4; ++index) {
723 if (c >= '0' && c <= '9')
725 else if (c >= 'a' && c <= 'f')
726 unicode += c - 'a' + 10;
727 else if (c >= 'A' && c <= 'F')
728 unicode += c - 'A' + 10;
731 "Bad unicode escape sequence in string: hexadecimal digit expected.",
734 ret_unicode = static_cast<unsigned int>(unicode);
738 bool Reader::addError(const String& message, Token& token, Location extra) {
741 info.message_ = message;
743 errors_.push_back(info);
747 bool Reader::recoverFromError(TokenType skipUntilToken) {
748 size_t const errorCount = errors_.size();
751 if (!readToken(skip))
752 errors_.resize(errorCount); // discard errors caused by recovery
753 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
756 errors_.resize(errorCount);
760 bool Reader::addErrorAndRecover(const String& message,
762 TokenType skipUntilToken) {
763 addError(message, token);
764 return recoverFromError(skipUntilToken);
767 Value& Reader::currentValue() { return *(nodes_.top()); }
769 Reader::Char Reader::getNextChar() {
770 if (current_ == end_)
775 void Reader::getLocationLineAndColumn(Location location,
778 Location current = begin_;
779 Location lastLineStart = current;
781 while (current < location && current != end_) {
784 if (*current == '\n')
786 lastLineStart = current;
788 } else if (c == '\n') {
789 lastLineStart = current;
793 // column & line start at 1
794 column = int(location - lastLineStart) + 1;
798 String Reader::getLocationLineAndColumn(Location location) const {
800 getLocationLineAndColumn(location, line, column);
801 char buffer[18 + 16 + 16 + 1];
802 jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
806 // Deprecated. Preserved for backward compatibility
807 String Reader::getFormatedErrorMessages() const {
808 return getFormattedErrorMessages();
811 String Reader::getFormattedErrorMessages() const {
812 String formattedMessage;
813 for (const auto& error : errors_) {
815 "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
816 formattedMessage += " " + error.message_ + "\n";
819 "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
821 return formattedMessage;
824 std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
825 std::vector<Reader::StructuredError> allErrors;
826 for (const auto& error : errors_) {
827 Reader::StructuredError structured;
828 structured.offset_start = error.token_.start_ - begin_;
829 structured.offset_limit = error.token_.end_ - begin_;
830 structured.message = error.message_;
831 allErrors.push_back(structured);
836 bool Reader::pushError(const Value& value, const String& message) {
837 ptrdiff_t const length = end_ - begin_;
838 if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
841 token.type_ = tokenError;
842 token.start_ = begin_ + value.getOffsetStart();
843 token.end_ = begin_ + value.getOffsetLimit();
846 info.message_ = message;
847 info.extra_ = nullptr;
848 errors_.push_back(info);
852 bool Reader::pushError(const Value& value,
853 const String& message,
854 const Value& extra) {
855 ptrdiff_t const length = end_ - begin_;
856 if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
857 extra.getOffsetLimit() > length)
860 token.type_ = tokenError;
861 token.start_ = begin_ + value.getOffsetStart();
862 token.end_ = begin_ + value.getOffsetLimit();
865 info.message_ = message;
866 info.extra_ = begin_ + extra.getOffsetStart();
867 errors_.push_back(info);
871 bool Reader::good() const { return errors_.empty(); }
873 // Originally copied from the Features class (now deprecated), used internally
874 // for features implementation.
877 static OurFeatures all();
880 bool allowDroppedNullPlaceholders_;
881 bool allowNumericKeys_;
882 bool allowSingleQuotes_;
885 bool allowSpecialFloats_;
889 OurFeatures OurFeatures::all() { return {}; }
891 // Implementation of class Reader
892 // ////////////////////////////////
894 // Originally copied from the Reader class (now deprecated), used internally
895 // for implementing JSON reading.
899 typedef const Char* Location;
900 struct StructuredError {
901 ptrdiff_t offset_start;
902 ptrdiff_t offset_limit;
906 OurReader(OurFeatures const& features);
907 bool parse(const char* beginDoc,
910 bool collectComments = true);
911 String getFormattedErrorMessages() const;
912 std::vector<StructuredError> getStructuredErrors() const;
913 bool pushError(const Value& value, const String& message);
914 bool pushError(const Value& value, const String& message, const Value& extra);
918 OurReader(OurReader const&); // no impl
919 void operator=(OurReader const&); // no impl
922 tokenEndOfStream = 0,
936 tokenMemberSeparator,
955 typedef std::deque<ErrorInfo> Errors;
957 bool readToken(Token& token);
959 bool match(Location pattern, int patternLength);
961 bool readCStyleComment();
962 bool readCppStyleComment();
964 bool readStringSingleQuote();
965 bool readNumber(bool checkInf);
967 bool readObject(Token& token);
968 bool readArray(Token& token);
969 bool decodeNumber(Token& token);
970 bool decodeNumber(Token& token, Value& decoded);
971 bool decodeString(Token& token);
972 bool decodeString(Token& token, String& decoded);
973 bool decodeDouble(Token& token);
974 bool decodeDouble(Token& token, Value& decoded);
975 bool decodeUnicodeCodePoint(Token& token,
978 unsigned int& unicode);
979 bool decodeUnicodeEscapeSequence(Token& token,
982 unsigned int& unicode);
983 bool addError(const String& message, Token& token, Location extra = nullptr);
984 bool recoverFromError(TokenType skipUntilToken);
985 bool addErrorAndRecover(const String& message,
987 TokenType skipUntilToken);
988 void skipUntilSpace();
989 Value& currentValue();
992 getLocationLineAndColumn(Location location, int& line, int& column) const;
993 String getLocationLineAndColumn(Location location) const;
994 void addComment(Location begin, Location end, CommentPlacement placement);
995 void skipCommentTokens(Token& token);
997 static String normalizeEOL(Location begin, Location end);
998 static bool containsNewLine(Location begin, Location end);
1000 typedef std::stack<Value*> Nodes;
1007 Location lastValueEnd_;
1009 String commentsBefore_;
1011 OurFeatures const features_;
1012 bool collectComments_;
1015 // complete copy of Read impl, for OurReader
1017 bool OurReader::containsNewLine(OurReader::Location begin,
1018 OurReader::Location end) {
1019 for (; begin < end; ++begin)
1020 if (*begin == '\n' || *begin == '\r')
1025 OurReader::OurReader(OurFeatures const& features)
1026 : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
1027 lastValue_(), commentsBefore_(), features_(features), collectComments_() {
1030 bool OurReader::parse(const char* beginDoc,
1033 bool collectComments) {
1034 if (!features_.allowComments_) {
1035 collectComments = false;
1040 collectComments_ = collectComments;
1042 lastValueEnd_ = nullptr;
1043 lastValue_ = nullptr;
1044 commentsBefore_.clear();
1046 while (!nodes_.empty())
1050 bool successful = readValue();
1052 skipCommentTokens(token);
1053 if (features_.failIfExtra_) {
1054 if ((features_.strictRoot_ || token.type_ != tokenError) &&
1055 token.type_ != tokenEndOfStream) {
1056 addError("Extra non-whitespace after JSON value.", token);
1060 if (collectComments_ && !commentsBefore_.empty())
1061 root.setComment(commentsBefore_, commentAfter);
1062 if (features_.strictRoot_) {
1063 if (!root.isArray() && !root.isObject()) {
1064 // Set error location to start of doc, ideally should be first token found
1066 token.type_ = tokenError;
1067 token.start_ = beginDoc;
1068 token.end_ = endDoc;
1070 "A valid JSON document must be either an array or an object value.",
1078 bool OurReader::readValue() {
1079 // To preserve the old behaviour we cast size_t to int.
1080 if (nodes_.size() > features_.stackLimit_)
1081 throwRuntimeError("Exceeded stackLimit in readValue().");
1083 skipCommentTokens(token);
1084 bool successful = true;
1086 if (collectComments_ && !commentsBefore_.empty()) {
1087 currentValue().setComment(commentsBefore_, commentBefore);
1088 commentsBefore_.clear();
1091 switch (token.type_) {
1092 case tokenObjectBegin:
1093 successful = readObject(token);
1094 currentValue().setOffsetLimit(current_ - begin_);
1096 case tokenArrayBegin:
1097 successful = readArray(token);
1098 currentValue().setOffsetLimit(current_ - begin_);
1101 successful = decodeNumber(token);
1104 successful = decodeString(token);
1108 currentValue().swapPayload(v);
1109 currentValue().setOffsetStart(token.start_ - begin_);
1110 currentValue().setOffsetLimit(token.end_ - begin_);
1114 currentValue().swapPayload(v);
1115 currentValue().setOffsetStart(token.start_ - begin_);
1116 currentValue().setOffsetLimit(token.end_ - begin_);
1120 currentValue().swapPayload(v);
1121 currentValue().setOffsetStart(token.start_ - begin_);
1122 currentValue().setOffsetLimit(token.end_ - begin_);
1125 Value v(std::numeric_limits<double>::quiet_NaN());
1126 currentValue().swapPayload(v);
1127 currentValue().setOffsetStart(token.start_ - begin_);
1128 currentValue().setOffsetLimit(token.end_ - begin_);
1131 Value v(std::numeric_limits<double>::infinity());
1132 currentValue().swapPayload(v);
1133 currentValue().setOffsetStart(token.start_ - begin_);
1134 currentValue().setOffsetLimit(token.end_ - begin_);
1137 Value v(-std::numeric_limits<double>::infinity());
1138 currentValue().swapPayload(v);
1139 currentValue().setOffsetStart(token.start_ - begin_);
1140 currentValue().setOffsetLimit(token.end_ - begin_);
1142 case tokenArraySeparator:
1143 case tokenObjectEnd:
1145 if (features_.allowDroppedNullPlaceholders_) {
1146 // "Un-read" the current token and mark the current value as a null
1150 currentValue().swapPayload(v);
1151 currentValue().setOffsetStart(current_ - begin_ - 1);
1152 currentValue().setOffsetLimit(current_ - begin_);
1154 } // else, fall through ...
1156 currentValue().setOffsetStart(token.start_ - begin_);
1157 currentValue().setOffsetLimit(token.end_ - begin_);
1158 return addError("Syntax error: value, object or array expected.", token);
1161 if (collectComments_) {
1162 lastValueEnd_ = current_;
1163 lastValue_ = ¤tValue();
1169 void OurReader::skipCommentTokens(Token& token) {
1170 if (features_.allowComments_) {
1173 } while (token.type_ == tokenComment);
1179 bool OurReader::readToken(Token& token) {
1181 token.start_ = current_;
1182 Char c = getNextChar();
1186 token.type_ = tokenObjectBegin;
1189 token.type_ = tokenObjectEnd;
1192 token.type_ = tokenArrayBegin;
1195 token.type_ = tokenArrayEnd;
1198 token.type_ = tokenString;
1202 if (features_.allowSingleQuotes_) {
1203 token.type_ = tokenString;
1204 ok = readStringSingleQuote();
1206 } // else fall through
1208 token.type_ = tokenComment;
1221 token.type_ = tokenNumber;
1225 if (readNumber(true)) {
1226 token.type_ = tokenNumber;
1228 token.type_ = tokenNegInf;
1229 ok = features_.allowSpecialFloats_ && match("nfinity", 7);
1233 token.type_ = tokenTrue;
1234 ok = match("rue", 3);
1237 token.type_ = tokenFalse;
1238 ok = match("alse", 4);
1241 token.type_ = tokenNull;
1242 ok = match("ull", 3);
1245 if (features_.allowSpecialFloats_) {
1246 token.type_ = tokenNaN;
1247 ok = match("aN", 2);
1253 if (features_.allowSpecialFloats_) {
1254 token.type_ = tokenPosInf;
1255 ok = match("nfinity", 7);
1261 token.type_ = tokenArraySeparator;
1264 token.type_ = tokenMemberSeparator;
1267 token.type_ = tokenEndOfStream;
1274 token.type_ = tokenError;
1275 token.end_ = current_;
1279 void OurReader::skipSpaces() {
1280 while (current_ != end_) {
1282 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1289 bool OurReader::match(Location pattern, int patternLength) {
1290 if (end_ - current_ < patternLength)
1292 int index = patternLength;
1294 if (current_[index] != pattern[index])
1296 current_ += patternLength;
1300 bool OurReader::readComment() {
1301 Location commentBegin = current_ - 1;
1302 Char c = getNextChar();
1303 bool successful = false;
1305 successful = readCStyleComment();
1307 successful = readCppStyleComment();
1311 if (collectComments_) {
1312 CommentPlacement placement = commentBefore;
1313 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1314 if (c != '*' || !containsNewLine(commentBegin, current_))
1315 placement = commentAfterOnSameLine;
1318 addComment(commentBegin, current_, placement);
1323 String OurReader::normalizeEOL(OurReader::Location begin,
1324 OurReader::Location end) {
1326 normalized.reserve(static_cast<size_t>(end - begin));
1327 OurReader::Location current = begin;
1328 while (current != end) {
1329 char c = *current++;
1331 if (current != end && *current == '\n')
1343 void OurReader::addComment(Location begin,
1345 CommentPlacement placement) {
1346 assert(collectComments_);
1347 const String& normalized = normalizeEOL(begin, end);
1348 if (placement == commentAfterOnSameLine) {
1349 assert(lastValue_ != nullptr);
1350 lastValue_->setComment(normalized, placement);
1352 commentsBefore_ += normalized;
1356 bool OurReader::readCStyleComment() {
1357 while ((current_ + 1) < end_) {
1358 Char c = getNextChar();
1359 if (c == '*' && *current_ == '/')
1362 return getNextChar() == '/';
1365 bool OurReader::readCppStyleComment() {
1366 while (current_ != end_) {
1367 Char c = getNextChar();
1371 // Consume DOS EOL. It will be normalized in addComment.
1372 if (current_ != end_ && *current_ == '\n')
1374 // Break on Moc OS 9 EOL.
1381 bool OurReader::readNumber(bool checkInf) {
1382 const char* p = current_;
1383 if (checkInf && p != end_ && *p == 'I') {
1387 char c = '0'; // stopgap for already consumed character
1389 while (c >= '0' && c <= '9')
1390 c = (current_ = p) < end_ ? *p++ : '\0';
1393 c = (current_ = p) < end_ ? *p++ : '\0';
1394 while (c >= '0' && c <= '9')
1395 c = (current_ = p) < end_ ? *p++ : '\0';
1398 if (c == 'e' || c == 'E') {
1399 c = (current_ = p) < end_ ? *p++ : '\0';
1400 if (c == '+' || c == '-')
1401 c = (current_ = p) < end_ ? *p++ : '\0';
1402 while (c >= '0' && c <= '9')
1403 c = (current_ = p) < end_ ? *p++ : '\0';
1407 bool OurReader::readString() {
1409 while (current_ != end_) {
1419 bool OurReader::readStringSingleQuote() {
1421 while (current_ != end_) {
1431 bool OurReader::readObject(Token& token) {
1434 Value init(objectValue);
1435 currentValue().swapPayload(init);
1436 currentValue().setOffsetStart(token.start_ - begin_);
1437 while (readToken(tokenName)) {
1438 bool initialTokenOk = true;
1439 while (tokenName.type_ == tokenComment && initialTokenOk)
1440 initialTokenOk = readToken(tokenName);
1441 if (!initialTokenOk)
1443 if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
1446 if (tokenName.type_ == tokenString) {
1447 if (!decodeString(tokenName, name))
1448 return recoverFromError(tokenObjectEnd);
1449 } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1451 if (!decodeNumber(tokenName, numberName))
1452 return recoverFromError(tokenObjectEnd);
1453 name = numberName.asString();
1457 if (name.length() >= (1U << 30))
1458 throwRuntimeError("keylength >= 2^30");
1459 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1460 String msg = "Duplicate key: '" + name + "'";
1461 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1465 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1466 return addErrorAndRecover("Missing ':' after object member name", colon,
1469 Value& value = currentValue()[name];
1470 nodes_.push(&value);
1471 bool ok = readValue();
1473 if (!ok) // error already set
1474 return recoverFromError(tokenObjectEnd);
1477 if (!readToken(comma) ||
1478 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
1479 comma.type_ != tokenComment)) {
1480 return addErrorAndRecover("Missing ',' or '}' in object declaration",
1481 comma, tokenObjectEnd);
1483 bool finalizeTokenOk = true;
1484 while (comma.type_ == tokenComment && finalizeTokenOk)
1485 finalizeTokenOk = readToken(comma);
1486 if (comma.type_ == tokenObjectEnd)
1489 return addErrorAndRecover("Missing '}' or object member name", tokenName,
1493 bool OurReader::readArray(Token& token) {
1494 Value init(arrayValue);
1495 currentValue().swapPayload(init);
1496 currentValue().setOffsetStart(token.start_ - begin_);
1498 if (current_ != end_ && *current_ == ']') // empty array
1501 readToken(endArray);
1506 Value& value = currentValue()[index++];
1507 nodes_.push(&value);
1508 bool ok = readValue();
1510 if (!ok) // error already set
1511 return recoverFromError(tokenArrayEnd);
1514 // Accept Comment after last item in the array.
1515 ok = readToken(currentToken);
1516 while (currentToken.type_ == tokenComment && ok) {
1517 ok = readToken(currentToken);
1519 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1520 currentToken.type_ != tokenArrayEnd);
1521 if (!ok || badTokenType) {
1522 return addErrorAndRecover("Missing ',' or ']' in array declaration",
1523 currentToken, tokenArrayEnd);
1525 if (currentToken.type_ == tokenArrayEnd)
1531 bool OurReader::decodeNumber(Token& token) {
1533 if (!decodeNumber(token, decoded))
1535 currentValue().swapPayload(decoded);
1536 currentValue().setOffsetStart(token.start_ - begin_);
1537 currentValue().setOffsetLimit(token.end_ - begin_);
1541 bool OurReader::decodeNumber(Token& token, Value& decoded) {
1542 // Attempts to parse the number as an integer. If the number is
1543 // larger than the maximum supported value of an integer then
1544 // we decode the number as a double.
1545 Location current = token.start_;
1546 bool isNegative = *current == '-';
1550 // TODO(issue #960): Change to constexpr
1551 static const auto positive_threshold = Value::maxLargestUInt / 10;
1552 static const auto positive_last_digit = Value::maxLargestUInt % 10;
1553 static const auto negative_threshold =
1554 Value::LargestUInt(Value::minLargestInt) / 10;
1555 static const auto negative_last_digit =
1556 Value::LargestUInt(Value::minLargestInt) % 10;
1558 const auto threshold = isNegative ? negative_threshold : positive_threshold;
1559 const auto last_digit =
1560 isNegative ? negative_last_digit : positive_last_digit;
1562 Value::LargestUInt value = 0;
1563 while (current < token.end_) {
1564 Char c = *current++;
1565 if (c < '0' || c > '9')
1566 return decodeDouble(token, decoded);
1568 const auto digit(static_cast<Value::UInt>(c - '0'));
1569 if (value >= threshold) {
1570 // We've hit or exceeded the max value divided by 10 (rounded down). If
1571 // a) we've only just touched the limit, meaing value == threshold,
1572 // b) this is the last digit, or
1573 // c) it's small enough to fit in that rounding delta, we're okay.
1574 // Otherwise treat this number as a double to avoid overflow.
1575 if (value > threshold || current != token.end_ || digit > last_digit) {
1576 return decodeDouble(token, decoded);
1579 value = value * 10 + digit;
1583 decoded = -Value::LargestInt(value);
1584 else if (value <= Value::LargestUInt(Value::maxLargestInt))
1585 decoded = Value::LargestInt(value);
1592 bool OurReader::decodeDouble(Token& token) {
1594 if (!decodeDouble(token, decoded))
1596 currentValue().swapPayload(decoded);
1597 currentValue().setOffsetStart(token.start_ - begin_);
1598 currentValue().setOffsetLimit(token.end_ - begin_);
1602 bool OurReader::decodeDouble(Token& token, Value& decoded) {
1604 const int bufferSize = 32;
1606 ptrdiff_t const length = token.end_ - token.start_;
1608 // Sanity check to avoid buffer overflow exploits.
1610 return addError("Unable to parse token length", token);
1612 auto const ulength = static_cast<size_t>(length);
1614 // Avoid using a string constant for the format control string given to
1615 // sscanf, as this can cause hard to debug crashes on OS X. See here for more
1618 // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
1619 char format[] = "%lf";
1621 if (length <= bufferSize) {
1622 Char buffer[bufferSize + 1];
1623 memcpy(buffer, token.start_, ulength);
1625 fixNumericLocaleInput(buffer, buffer + length);
1626 count = sscanf(buffer, format, &value);
1628 String buffer(token.start_, token.end_);
1629 count = sscanf(buffer.c_str(), format, &value);
1634 "'" + String(token.start_, token.end_) + "' is not a number.", token);
1639 bool OurReader::decodeString(Token& token) {
1640 String decoded_string;
1641 if (!decodeString(token, decoded_string))
1643 Value decoded(decoded_string);
1644 currentValue().swapPayload(decoded);
1645 currentValue().setOffsetStart(token.start_ - begin_);
1646 currentValue().setOffsetLimit(token.end_ - begin_);
1650 bool OurReader::decodeString(Token& token, String& decoded) {
1651 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
1652 Location current = token.start_ + 1; // skip '"'
1653 Location end = token.end_ - 1; // do not include '"'
1654 while (current != end) {
1655 Char c = *current++;
1658 else if (c == '\\') {
1660 return addError("Empty escape sequence in string", token, current);
1661 Char escape = *current++;
1688 unsigned int unicode;
1689 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1691 decoded += codePointToUTF8(unicode);
1694 return addError("Bad escape sequence in string", token, current);
1703 bool OurReader::decodeUnicodeCodePoint(Token& token,
1706 unsigned int& unicode) {
1708 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1710 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1712 if (end - current < 6)
1714 "additional six characters expected to parse unicode surrogate pair.",
1716 if (*(current++) == '\\' && *(current++) == 'u') {
1717 unsigned int surrogatePair;
1718 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1719 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1723 return addError("expecting another \\u token to begin the second half of "
1724 "a unicode surrogate pair",
1730 bool OurReader::decodeUnicodeEscapeSequence(Token& token,
1733 unsigned int& ret_unicode) {
1734 if (end - current < 4)
1736 "Bad unicode escape sequence in string: four digits expected.", token,
1739 for (int index = 0; index < 4; ++index) {
1740 Char c = *current++;
1742 if (c >= '0' && c <= '9')
1744 else if (c >= 'a' && c <= 'f')
1745 unicode += c - 'a' + 10;
1746 else if (c >= 'A' && c <= 'F')
1747 unicode += c - 'A' + 10;
1750 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1753 ret_unicode = static_cast<unsigned int>(unicode);
1757 bool OurReader::addError(const String& message, Token& token, Location extra) {
1759 info.token_ = token;
1760 info.message_ = message;
1761 info.extra_ = extra;
1762 errors_.push_back(info);
1766 bool OurReader::recoverFromError(TokenType skipUntilToken) {
1767 size_t errorCount = errors_.size();
1770 if (!readToken(skip))
1771 errors_.resize(errorCount); // discard errors caused by recovery
1772 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1775 errors_.resize(errorCount);
1779 bool OurReader::addErrorAndRecover(const String& message,
1781 TokenType skipUntilToken) {
1782 addError(message, token);
1783 return recoverFromError(skipUntilToken);
1786 Value& OurReader::currentValue() { return *(nodes_.top()); }
1788 OurReader::Char OurReader::getNextChar() {
1789 if (current_ == end_)
1794 void OurReader::getLocationLineAndColumn(Location location,
1796 int& column) const {
1797 Location current = begin_;
1798 Location lastLineStart = current;
1800 while (current < location && current != end_) {
1801 Char c = *current++;
1803 if (*current == '\n')
1805 lastLineStart = current;
1807 } else if (c == '\n') {
1808 lastLineStart = current;
1812 // column & line start at 1
1813 column = int(location - lastLineStart) + 1;
1817 String OurReader::getLocationLineAndColumn(Location location) const {
1819 getLocationLineAndColumn(location, line, column);
1820 char buffer[18 + 16 + 16 + 1];
1821 jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1825 String OurReader::getFormattedErrorMessages() const {
1826 String formattedMessage;
1827 for (const auto& error : errors_) {
1829 "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
1830 formattedMessage += " " + error.message_ + "\n";
1833 "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
1835 return formattedMessage;
1838 std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
1839 std::vector<OurReader::StructuredError> allErrors;
1840 for (const auto& error : errors_) {
1841 OurReader::StructuredError structured;
1842 structured.offset_start = error.token_.start_ - begin_;
1843 structured.offset_limit = error.token_.end_ - begin_;
1844 structured.message = error.message_;
1845 allErrors.push_back(structured);
1850 bool OurReader::pushError(const Value& value, const String& message) {
1851 ptrdiff_t length = end_ - begin_;
1852 if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
1855 token.type_ = tokenError;
1856 token.start_ = begin_ + value.getOffsetStart();
1857 token.end_ = begin_ + value.getOffsetLimit();
1859 info.token_ = token;
1860 info.message_ = message;
1861 info.extra_ = nullptr;
1862 errors_.push_back(info);
1866 bool OurReader::pushError(const Value& value,
1867 const String& message,
1868 const Value& extra) {
1869 ptrdiff_t length = end_ - begin_;
1870 if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
1871 extra.getOffsetLimit() > length)
1874 token.type_ = tokenError;
1875 token.start_ = begin_ + value.getOffsetStart();
1876 token.end_ = begin_ + value.getOffsetLimit();
1878 info.token_ = token;
1879 info.message_ = message;
1880 info.extra_ = begin_ + extra.getOffsetStart();
1881 errors_.push_back(info);
1885 bool OurReader::good() const { return errors_.empty(); }
1887 class OurCharReader : public CharReader {
1888 bool const collectComments_;
1892 OurCharReader(bool collectComments, OurFeatures const& features)
1893 : collectComments_(collectComments), reader_(features) {}
1894 bool parse(char const* beginDoc,
1897 String* errs) override {
1898 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1900 *errs = reader_.getFormattedErrorMessages();
1906 CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
1907 CharReaderBuilder::~CharReaderBuilder() = default;
1908 CharReader* CharReaderBuilder::newCharReader() const {
1909 bool collectComments = settings_["collectComments"].asBool();
1910 OurFeatures features = OurFeatures::all();
1911 features.allowComments_ = settings_["allowComments"].asBool();
1912 features.strictRoot_ = settings_["strictRoot"].asBool();
1913 features.allowDroppedNullPlaceholders_ =
1914 settings_["allowDroppedNullPlaceholders"].asBool();
1915 features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
1916 features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
1918 // Stack limit is always a size_t, so we get this as an unsigned int
1919 // regardless of it we have 64-bit integer support enabled.
1920 features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
1921 features.failIfExtra_ = settings_["failIfExtra"].asBool();
1922 features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
1923 features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
1924 return new OurCharReader(collectComments, features);
1926 static void getValidReaderKeys(std::set<String>* valid_keys) {
1927 valid_keys->clear();
1928 valid_keys->insert("collectComments");
1929 valid_keys->insert("allowComments");
1930 valid_keys->insert("strictRoot");
1931 valid_keys->insert("allowDroppedNullPlaceholders");
1932 valid_keys->insert("allowNumericKeys");
1933 valid_keys->insert("allowSingleQuotes");
1934 valid_keys->insert("stackLimit");
1935 valid_keys->insert("failIfExtra");
1936 valid_keys->insert("rejectDupKeys");
1937 valid_keys->insert("allowSpecialFloats");
1939 bool CharReaderBuilder::validate(Json::Value* invalid) const {
1940 Json::Value my_invalid;
1942 invalid = &my_invalid; // so we do not need to test for NULL
1943 Json::Value& inv = *invalid;
1944 std::set<String> valid_keys;
1945 getValidReaderKeys(&valid_keys);
1946 Value::Members keys = settings_.getMemberNames();
1947 size_t n = keys.size();
1948 for (size_t i = 0; i < n; ++i) {
1949 String const& key = keys[i];
1950 if (valid_keys.find(key) == valid_keys.end()) {
1951 inv[key] = settings_[key];
1956 Value& CharReaderBuilder::operator[](const String& key) {
1957 return settings_[key];
1960 void CharReaderBuilder::strictMode(Json::Value* settings) {
1961 //! [CharReaderBuilderStrictMode]
1962 (*settings)["allowComments"] = false;
1963 (*settings)["strictRoot"] = true;
1964 (*settings)["allowDroppedNullPlaceholders"] = false;
1965 (*settings)["allowNumericKeys"] = false;
1966 (*settings)["allowSingleQuotes"] = false;
1967 (*settings)["stackLimit"] = 1000;
1968 (*settings)["failIfExtra"] = true;
1969 (*settings)["rejectDupKeys"] = true;
1970 (*settings)["allowSpecialFloats"] = false;
1971 //! [CharReaderBuilderStrictMode]
1974 void CharReaderBuilder::setDefaults(Json::Value* settings) {
1975 //! [CharReaderBuilderDefaults]
1976 (*settings)["collectComments"] = true;
1977 (*settings)["allowComments"] = true;
1978 (*settings)["strictRoot"] = false;
1979 (*settings)["allowDroppedNullPlaceholders"] = false;
1980 (*settings)["allowNumericKeys"] = false;
1981 (*settings)["allowSingleQuotes"] = false;
1982 (*settings)["stackLimit"] = 1000;
1983 (*settings)["failIfExtra"] = false;
1984 (*settings)["rejectDupKeys"] = false;
1985 (*settings)["allowSpecialFloats"] = false;
1986 //! [CharReaderBuilderDefaults]
1989 //////////////////////////////////
1992 bool parseFromStream(CharReader::Factory const& fact,
1997 ssin << sin.rdbuf();
1998 String doc = ssin.str();
1999 char const* begin = doc.data();
2000 char const* end = begin + doc.size();
2001 // Note that we do not actually need a null-terminator.
2002 CharReaderPtr const reader(fact.newCharReader());
2003 return reader->parse(begin, end, root, errs);
2006 IStream& operator>>(IStream& sin, Value& root) {
2007 CharReaderBuilder b;
2009 bool ok = parseFromStream(b, sin, &root, &errs);
2011 throwRuntimeError(errs);