JsonCpp project page JsonCpp home page

json_reader.cpp
Go to the documentation of this file.
1 // Copyright 2007-2011 Baptiste Lepilleur
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5 
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include <json/assertions.h>
8 #include <json/reader.h>
9 #include <json/value.h>
10 #include "json_tool.h"
11 #endif // if !defined(JSON_IS_AMALGAMATION)
12 #include <utility>
13 #include <cstdio>
14 #include <cassert>
15 #include <cstring>
16 #include <istream>
17 #include <sstream>
18 #include <memory>
19 #include <set>
20 
21 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
22 #define snprintf _snprintf
23 #endif
24 
25 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
26 // Disable warning about strdup being deprecated.
27 #pragma warning(disable : 4996)
28 #endif
29 
30 static int const stackLimit_g = 1000;
31 static int stackDepth_g = 0; // see readValue()
32 
33 namespace Json {
34 
35 typedef std::auto_ptr<CharReader> CharReaderPtr;
36 
37 // Implementation of class Features
38 // ////////////////////////////////
39 
41  : allowComments_(true), strictRoot_(false)
42 {}
44 
46  Features features;
47  features.allowComments_ = false;
48  features.strictRoot_ = true;
49  return features;
50 }
51 
52 // Implementation of class Reader
53 // ////////////////////////////////
54 
56  for (; begin < end; ++begin)
57  if (*begin == '\n' || *begin == '\r')
58  return true;
59  return false;
60 }
61 
62 // Class Reader
63 // //////////////////////////////////////////////////////////////////
64 
66  : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
67  lastValue_(), commentsBefore_(), features_(Features::all()),
68  collectComments_() {}
69 
70 Reader::Reader(const Features& features)
71  : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
72  lastValue_(), commentsBefore_(), features_(features), collectComments_() {
73 }
74 
75 bool
76 Reader::parse(const std::string& document, Value& root, bool collectComments) {
77  document_ = document;
78  const char* begin = document_.c_str();
79  const char* end = begin + document_.length();
80  return parse(begin, end, root, collectComments);
81 }
82 
83 bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
84  // std::istream_iterator<char> begin(sin);
85  // std::istream_iterator<char> end;
86  // Those would allow streamed input from a file, if parse() were a
87  // template function.
88 
89  // Since std::string is reference-counted, this at least does not
90  // create an extra copy.
91  std::string doc;
92  std::getline(sin, doc, (char)EOF);
93  return parse(doc, root, collectComments);
94 }
95 
96 bool Reader::parse(const char* beginDoc,
97  const char* endDoc,
98  Value& root,
99  bool collectComments) {
100  if (!features_.allowComments_) {
101  collectComments = false;
102  }
103 
104  begin_ = beginDoc;
105  end_ = endDoc;
106  collectComments_ = collectComments;
107  current_ = begin_;
108  lastValueEnd_ = 0;
109  lastValue_ = 0;
110  commentsBefore_ = "";
111  errors_.clear();
112  while (!nodes_.empty())
113  nodes_.pop();
114  nodes_.push(&root);
115 
116  stackDepth_g = 0; // Yes, this is bad coding, but options are limited.
117  bool successful = readValue();
118  Token token;
119  skipCommentTokens(token);
120  if (collectComments_ && !commentsBefore_.empty())
121  root.setComment(commentsBefore_, commentAfter);
122  if (features_.strictRoot_) {
123  if (!root.isArray() && !root.isObject()) {
124  // Set error location to start of doc, ideally should be first token found
125  // in doc
126  token.type_ = tokenError;
127  token.start_ = beginDoc;
128  token.end_ = endDoc;
129  addError(
130  "A valid JSON document must be either an array or an object value.",
131  token);
132  return false;
133  }
134  }
135  return successful;
136 }
137 
138 bool Reader::readValue() {
139  // This is a non-reentrant way to support a stackLimit. Terrible!
140  // But this deprecated class has a security problem: Bad input can
141  // cause a seg-fault. This seems like a fair, binary-compatible way
142  // to prevent the problem.
143  if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
144  ++stackDepth_g;
145 
146  Token token;
147  skipCommentTokens(token);
148  bool successful = true;
149 
150  if (collectComments_ && !commentsBefore_.empty()) {
151  currentValue().setComment(commentsBefore_, commentBefore);
152  commentsBefore_ = "";
153  }
154 
155  switch (token.type_) {
156  case tokenObjectBegin:
157  successful = readObject(token);
158  break;
159  case tokenArrayBegin:
160  successful = readArray(token);
161  break;
162  case tokenNumber:
163  successful = decodeNumber(token);
164  break;
165  case tokenString:
166  successful = decodeString(token);
167  break;
168  case tokenTrue:
169  {
170  Value v(true);
171  currentValue().swapPayload(v);
172  }
173  break;
174  case tokenFalse:
175  {
176  Value v(false);
177  currentValue().swapPayload(v);
178  }
179  break;
180  case tokenNull:
181  {
182  Value v;
183  currentValue().swapPayload(v);
184  }
185  break;
186  // Else, fall through...
187  default:
188  return addError("Syntax error: value, object or array expected.", token);
189  }
190 
191  if (collectComments_) {
192  lastValueEnd_ = current_;
193  lastValue_ = &currentValue();
194  }
195 
196  --stackDepth_g;
197  return successful;
198 }
199 
200 void Reader::skipCommentTokens(Token& token) {
201  if (features_.allowComments_) {
202  do {
203  readToken(token);
204  } while (token.type_ == tokenComment);
205  } else {
206  readToken(token);
207  }
208 }
209 
210 bool Reader::readToken(Token& token) {
211  skipSpaces();
212  token.start_ = current_;
213  Char c = getNextChar();
214  bool ok = true;
215  switch (c) {
216  case '{':
217  token.type_ = tokenObjectBegin;
218  break;
219  case '}':
220  token.type_ = tokenObjectEnd;
221  break;
222  case '[':
223  token.type_ = tokenArrayBegin;
224  break;
225  case ']':
226  token.type_ = tokenArrayEnd;
227  break;
228  case '"':
229  token.type_ = tokenString;
230  ok = readString();
231  break;
232  case '/':
233  token.type_ = tokenComment;
234  ok = readComment();
235  break;
236  case '0':
237  case '1':
238  case '2':
239  case '3':
240  case '4':
241  case '5':
242  case '6':
243  case '7':
244  case '8':
245  case '9':
246  case '-':
247  token.type_ = tokenNumber;
248  readNumber();
249  break;
250  case 't':
251  token.type_ = tokenTrue;
252  ok = match("rue", 3);
253  break;
254  case 'f':
255  token.type_ = tokenFalse;
256  ok = match("alse", 4);
257  break;
258  case 'n':
259  token.type_ = tokenNull;
260  ok = match("ull", 3);
261  break;
262  case ',':
263  token.type_ = tokenArraySeparator;
264  break;
265  case ':':
266  token.type_ = tokenMemberSeparator;
267  break;
268  case 0:
269  token.type_ = tokenEndOfStream;
270  break;
271  default:
272  ok = false;
273  break;
274  }
275  if (!ok)
276  token.type_ = tokenError;
277  token.end_ = current_;
278  return true;
279 }
280 
281 void Reader::skipSpaces() {
282  while (current_ != end_) {
283  Char c = *current_;
284  if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
285  ++current_;
286  else
287  break;
288  }
289 }
290 
291 bool Reader::match(Location pattern, int patternLength) {
292  if (end_ - current_ < patternLength)
293  return false;
294  int index = patternLength;
295  while (index--)
296  if (current_[index] != pattern[index])
297  return false;
298  current_ += patternLength;
299  return true;
300 }
301 
302 bool Reader::readComment() {
303  Location commentBegin = current_ - 1;
304  Char c = getNextChar();
305  bool successful = false;
306  if (c == '*')
307  successful = readCStyleComment();
308  else if (c == '/')
309  successful = readCppStyleComment();
310  if (!successful)
311  return false;
312 
313  if (collectComments_) {
314  CommentPlacement placement = commentBefore;
315  if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
316  if (c != '*' || !containsNewLine(commentBegin, current_))
317  placement = commentAfterOnSameLine;
318  }
319 
320  addComment(commentBegin, current_, placement);
321  }
322  return true;
323 }
324 
325 static std::string normalizeEOL(Reader::Location begin, Reader::Location end) {
326  std::string normalized;
327  normalized.reserve(end - begin);
328  Reader::Location current = begin;
329  while (current != end) {
330  char c = *current++;
331  if (c == '\r') {
332  if (current != end && *current == '\n')
333  // convert dos EOL
334  ++current;
335  // convert Mac EOL
336  normalized += '\n';
337  } else {
338  normalized += c;
339  }
340  }
341  return normalized;
342 }
343 
344 void
345 Reader::addComment(Location begin, Location end, CommentPlacement placement) {
346  assert(collectComments_);
347  const std::string& normalized = normalizeEOL(begin, end);
348  if (placement == commentAfterOnSameLine) {
349  assert(lastValue_ != 0);
350  lastValue_->setComment(normalized, placement);
351  } else {
352  commentsBefore_ += normalized;
353  }
354 }
355 
356 bool Reader::readCStyleComment() {
357  while (current_ != end_) {
358  Char c = getNextChar();
359  if (c == '*' && *current_ == '/')
360  break;
361  }
362  return getNextChar() == '/';
363 }
364 
365 bool Reader::readCppStyleComment() {
366  while (current_ != end_) {
367  Char c = getNextChar();
368  if (c == '\n')
369  break;
370  if (c == '\r') {
371  // Consume DOS EOL. It will be normalized in addComment.
372  if (current_ != end_ && *current_ == '\n')
373  getNextChar();
374  // Break on Moc OS 9 EOL.
375  break;
376  }
377  }
378  return true;
379 }
380 
381 void Reader::readNumber() {
382  const char *p = current_;
383  char c = '0'; // stopgap for already consumed character
384  // integral part
385  while (c >= '0' && c <= '9')
386  c = (current_ = p) < end_ ? *p++ : 0;
387  // fractional part
388  if (c == '.') {
389  c = (current_ = p) < end_ ? *p++ : 0;
390  while (c >= '0' && c <= '9')
391  c = (current_ = p) < end_ ? *p++ : 0;
392  }
393  // exponential part
394  if (c == 'e' || c == 'E') {
395  c = (current_ = p) < end_ ? *p++ : 0;
396  if (c == '+' || c == '-')
397  c = (current_ = p) < end_ ? *p++ : 0;
398  while (c >= '0' && c <= '9')
399  c = (current_ = p) < end_ ? *p++ : 0;
400  }
401 }
402 
403 bool Reader::readString() {
404  Char c = 0;
405  while (current_ != end_) {
406  c = getNextChar();
407  if (c == '\\')
408  getNextChar();
409  else if (c == '"')
410  break;
411  }
412  return c == '"';
413 }
414 
415 bool Reader::readObject(Token& /*tokenStart*/) {
416  Token tokenName;
417  std::string name;
418  Value init(objectValue);
419  currentValue().swapPayload(init);
420  while (readToken(tokenName)) {
421  bool initialTokenOk = true;
422  while (tokenName.type_ == tokenComment && initialTokenOk)
423  initialTokenOk = readToken(tokenName);
424  if (!initialTokenOk)
425  break;
426  if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
427  return true;
428  name = "";
429  if (tokenName.type_ == tokenString) {
430  if (!decodeString(tokenName, name))
431  return recoverFromError(tokenObjectEnd);
432  } else {
433  break;
434  }
435 
436  Token colon;
437  if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
438  return addErrorAndRecover(
439  "Missing ':' after object member name", colon, tokenObjectEnd);
440  }
441  Value& value = currentValue()[name];
442  nodes_.push(&value);
443  bool ok = readValue();
444  nodes_.pop();
445  if (!ok) // error already set
446  return recoverFromError(tokenObjectEnd);
447 
448  Token comma;
449  if (!readToken(comma) ||
450  (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
451  comma.type_ != tokenComment)) {
452  return addErrorAndRecover(
453  "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
454  }
455  bool finalizeTokenOk = true;
456  while (comma.type_ == tokenComment && finalizeTokenOk)
457  finalizeTokenOk = readToken(comma);
458  if (comma.type_ == tokenObjectEnd)
459  return true;
460  }
461  return addErrorAndRecover(
462  "Missing '}' or object member name", tokenName, tokenObjectEnd);
463 }
464 
465 bool Reader::readArray(Token& /*tokenStart*/) {
466  Value init(arrayValue);
467  currentValue().swapPayload(init);
468  skipSpaces();
469  if (*current_ == ']') // empty array
470  {
471  Token endArray;
472  readToken(endArray);
473  return true;
474  }
475  int index = 0;
476  for (;;) {
477  Value& value = currentValue()[index++];
478  nodes_.push(&value);
479  bool ok = readValue();
480  nodes_.pop();
481  if (!ok) // error already set
482  return recoverFromError(tokenArrayEnd);
483 
484  Token token;
485  // Accept Comment after last item in the array.
486  ok = readToken(token);
487  while (token.type_ == tokenComment && ok) {
488  ok = readToken(token);
489  }
490  bool badTokenType =
491  (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
492  if (!ok || badTokenType) {
493  return addErrorAndRecover(
494  "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
495  }
496  if (token.type_ == tokenArrayEnd)
497  break;
498  }
499  return true;
500 }
501 
502 bool Reader::decodeNumber(Token& token) {
503  Value decoded;
504  if (!decodeNumber(token, decoded))
505  return false;
506  currentValue().swapPayload(decoded);
507  return true;
508 }
509 
510 bool Reader::decodeNumber(Token& token, Value& decoded) {
511  // Attempts to parse the number as an integer. If the number is
512  // larger than the maximum supported value of an integer then
513  // we decode the number as a double.
514  Location current = token.start_;
515  bool isNegative = *current == '-';
516  if (isNegative)
517  ++current;
518  // TODO: Help the compiler do the div and mod at compile time or get rid of them.
519  Value::LargestUInt maxIntegerValue =
521  : Value::maxLargestUInt;
522  Value::LargestUInt threshold = maxIntegerValue / 10;
523  Value::LargestUInt value = 0;
524  while (current < token.end_) {
525  Char c = *current++;
526  if (c < '0' || c > '9')
527  return decodeDouble(token, decoded);
528  Value::UInt digit(c - '0');
529  if (value >= threshold) {
530  // We've hit or exceeded the max value divided by 10 (rounded down). If
531  // a) we've only just touched the limit, b) this is the last digit, and
532  // c) it's small enough to fit in that rounding delta, we're okay.
533  // Otherwise treat this number as a double to avoid overflow.
534  if (value > threshold || current != token.end_ ||
535  digit > maxIntegerValue % 10) {
536  return decodeDouble(token, decoded);
537  }
538  }
539  value = value * 10 + digit;
540  }
541  if (isNegative)
542  decoded = -Value::LargestInt(value);
543  else if (value <= Value::LargestUInt(Value::maxInt))
544  decoded = Value::LargestInt(value);
545  else
546  decoded = value;
547  return true;
548 }
549 
550 bool Reader::decodeDouble(Token& token) {
551  Value decoded;
552  if (!decodeDouble(token, decoded))
553  return false;
554  currentValue().swapPayload(decoded);
555  return true;
556 }
557 
558 bool Reader::decodeDouble(Token& token, Value& decoded) {
559  double value = 0;
560  std::string buffer(token.start_, token.end_);
561  std::istringstream is(buffer);
562  if (!(is >> value))
563  return addError("'" + std::string(token.start_, token.end_) +
564  "' is not a number.",
565  token);
566  decoded = value;
567  return true;
568 }
569 
570 bool Reader::decodeString(Token& token) {
571  std::string decoded_string;
572  if (!decodeString(token, decoded_string))
573  return false;
574  Value decoded(decoded_string);
575  currentValue().swapPayload(decoded);
576  return true;
577 }
578 
579 bool Reader::decodeString(Token& token, std::string& decoded) {
580  decoded.reserve(token.end_ - token.start_ - 2);
581  Location current = token.start_ + 1; // skip '"'
582  Location end = token.end_ - 1; // do not include '"'
583  while (current != end) {
584  Char c = *current++;
585  if (c == '"')
586  break;
587  else if (c == '\\') {
588  if (current == end)
589  return addError("Empty escape sequence in string", token, current);
590  Char escape = *current++;
591  switch (escape) {
592  case '"':
593  decoded += '"';
594  break;
595  case '/':
596  decoded += '/';
597  break;
598  case '\\':
599  decoded += '\\';
600  break;
601  case 'b':
602  decoded += '\b';
603  break;
604  case 'f':
605  decoded += '\f';
606  break;
607  case 'n':
608  decoded += '\n';
609  break;
610  case 'r':
611  decoded += '\r';
612  break;
613  case 't':
614  decoded += '\t';
615  break;
616  case 'u': {
617  unsigned int unicode;
618  if (!decodeUnicodeCodePoint(token, current, end, unicode))
619  return false;
620  decoded += codePointToUTF8(unicode);
621  } break;
622  default:
623  return addError("Bad escape sequence in string", token, current);
624  }
625  } else {
626  decoded += c;
627  }
628  }
629  return true;
630 }
631 
632 bool Reader::decodeUnicodeCodePoint(Token& token,
633  Location& current,
634  Location end,
635  unsigned int& unicode) {
636 
637  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
638  return false;
639  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
640  // surrogate pairs
641  if (end - current < 6)
642  return addError(
643  "additional six characters expected to parse unicode surrogate pair.",
644  token,
645  current);
646  unsigned int surrogatePair;
647  if (*(current++) == '\\' && *(current++) == 'u') {
648  if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
649  unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
650  } else
651  return false;
652  } else
653  return addError("expecting another \\u token to begin the second half of "
654  "a unicode surrogate pair",
655  token,
656  current);
657  }
658  return true;
659 }
660 
661 bool Reader::decodeUnicodeEscapeSequence(Token& token,
662  Location& current,
663  Location end,
664  unsigned int& unicode) {
665  if (end - current < 4)
666  return addError(
667  "Bad unicode escape sequence in string: four digits expected.",
668  token,
669  current);
670  unicode = 0;
671  for (int index = 0; index < 4; ++index) {
672  Char c = *current++;
673  unicode *= 16;
674  if (c >= '0' && c <= '9')
675  unicode += c - '0';
676  else if (c >= 'a' && c <= 'f')
677  unicode += c - 'a' + 10;
678  else if (c >= 'A' && c <= 'F')
679  unicode += c - 'A' + 10;
680  else
681  return addError(
682  "Bad unicode escape sequence in string: hexadecimal digit expected.",
683  token,
684  current);
685  }
686  return true;
687 }
688 
689 bool
690 Reader::addError(const std::string& message, Token& token, Location extra) {
691  ErrorInfo info;
692  info.token_ = token;
693  info.message_ = message;
694  info.extra_ = extra;
695  errors_.push_back(info);
696  return false;
697 }
698 
699 bool Reader::recoverFromError(TokenType skipUntilToken) {
700  int errorCount = int(errors_.size());
701  Token skip;
702  for (;;) {
703  if (!readToken(skip))
704  errors_.resize(errorCount); // discard errors caused by recovery
705  if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
706  break;
707  }
708  errors_.resize(errorCount);
709  return false;
710 }
711 
712 bool Reader::addErrorAndRecover(const std::string& message,
713  Token& token,
714  TokenType skipUntilToken) {
715  addError(message, token);
716  return recoverFromError(skipUntilToken);
717 }
718 
719 Value& Reader::currentValue() { return *(nodes_.top()); }
720 
721 Reader::Char Reader::getNextChar() {
722  if (current_ == end_)
723  return 0;
724  return *current_++;
725 }
726 
727 void Reader::getLocationLineAndColumn(Location location,
728  int& line,
729  int& column) const {
730  Location current = begin_;
731  Location lastLineStart = current;
732  line = 0;
733  while (current < location && current != end_) {
734  Char c = *current++;
735  if (c == '\r') {
736  if (*current == '\n')
737  ++current;
738  lastLineStart = current;
739  ++line;
740  } else if (c == '\n') {
741  lastLineStart = current;
742  ++line;
743  }
744  }
745  // column & line start at 1
746  column = int(location - lastLineStart) + 1;
747  ++line;
748 }
749 
750 std::string Reader::getLocationLineAndColumn(Location location) const {
751  int line, column;
752  getLocationLineAndColumn(location, line, column);
753  char buffer[18 + 16 + 16 + 1];
754 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
755 #if defined(WINCE)
756  _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
757 #else
758  sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
759 #endif
760 #else
761  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
762 #endif
763  return buffer;
764 }
765 
766 // Deprecated. Preserved for backward compatibility
767 std::string Reader::getFormatedErrorMessages() const {
768  return getFormattedErrorMessages();
769 }
770 
772  std::string formattedMessage;
773  for (Errors::const_iterator itError = errors_.begin();
774  itError != errors_.end();
775  ++itError) {
776  const ErrorInfo& error = *itError;
777  formattedMessage +=
778  "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
779  formattedMessage += " " + error.message_ + "\n";
780  if (error.extra_)
781  formattedMessage +=
782  "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
783  }
784  return formattedMessage;
785 }
786 
787 // Reader
789 
790 // exact copy of Features
791 class OurFeatures {
792 public:
793  static OurFeatures all();
794  OurFeatures();
795  bool allowComments_;
796  bool strictRoot_;
797  bool allowDroppedNullPlaceholders_;
798  bool allowNumericKeys_;
799  bool allowSingleQuotes_;
800  bool failIfExtra_;
801  bool rejectDupKeys_;
802  int stackLimit_;
803 }; // OurFeatures
804 
805 // exact copy of Implementation of class Features
806 // ////////////////////////////////
807 
808 OurFeatures::OurFeatures()
809  : allowComments_(true), strictRoot_(false)
810  , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)
811  , allowSingleQuotes_(false)
812  , failIfExtra_(false)
813 {
814 }
815 
816 OurFeatures OurFeatures::all() { return OurFeatures(); }
817 
818 // Implementation of class Reader
819 // ////////////////////////////////
820 
821 // exact copy of Reader, renamed to OurReader
822 class OurReader {
823 public:
824  typedef char Char;
825  typedef const Char* Location;
826  struct StructuredError {
827  size_t offset_start;
828  size_t offset_limit;
829  std::string message;
830  };
831 
832  OurReader(OurFeatures const& features);
833  bool parse(const char* beginDoc,
834  const char* endDoc,
835  Value& root,
836  bool collectComments = true);
837  std::string getFormattedErrorMessages() const;
838 
839 private:
840  OurReader(OurReader const&); // no impl
841  void operator=(OurReader const&); // no impl
842 
843  enum TokenType {
844  tokenEndOfStream = 0,
845  tokenObjectBegin,
846  tokenObjectEnd,
847  tokenArrayBegin,
848  tokenArrayEnd,
849  tokenString,
850  tokenNumber,
851  tokenTrue,
852  tokenFalse,
853  tokenNull,
854  tokenArraySeparator,
855  tokenMemberSeparator,
856  tokenComment,
857  tokenError
858  };
859 
860  class Token {
861  public:
862  TokenType type_;
863  Location start_;
864  Location end_;
865  };
866 
867  class ErrorInfo {
868  public:
869  Token token_;
870  std::string message_;
871  Location extra_;
872  };
873 
874  typedef std::deque<ErrorInfo> Errors;
875 
876  bool readToken(Token& token);
877  void skipSpaces();
878  bool match(Location pattern, int patternLength);
879  bool readComment();
880  bool readCStyleComment();
881  bool readCppStyleComment();
882  bool readString();
883  bool readStringSingleQuote();
884  void readNumber();
885  bool readValue();
886  bool readObject(Token& token);
887  bool readArray(Token& token);
888  bool decodeNumber(Token& token);
889  bool decodeNumber(Token& token, Value& decoded);
890  bool decodeString(Token& token);
891  bool decodeString(Token& token, std::string& decoded);
892  bool decodeDouble(Token& token);
893  bool decodeDouble(Token& token, Value& decoded);
894  bool decodeUnicodeCodePoint(Token& token,
895  Location& current,
896  Location end,
897  unsigned int& unicode);
898  bool decodeUnicodeEscapeSequence(Token& token,
899  Location& current,
900  Location end,
901  unsigned int& unicode);
902  bool addError(const std::string& message, Token& token, Location extra = 0);
903  bool recoverFromError(TokenType skipUntilToken);
904  bool addErrorAndRecover(const std::string& message,
905  Token& token,
906  TokenType skipUntilToken);
907  void skipUntilSpace();
908  Value& currentValue();
909  Char getNextChar();
910  void
911  getLocationLineAndColumn(Location location, int& line, int& column) const;
912  std::string getLocationLineAndColumn(Location location) const;
913  void addComment(Location begin, Location end, CommentPlacement placement);
914  void skipCommentTokens(Token& token);
915 
916  typedef std::stack<Value*> Nodes;
917  Nodes nodes_;
918  Errors errors_;
919  std::string document_;
920  Location begin_;
921  Location end_;
922  Location current_;
923  Location lastValueEnd_;
924  Value* lastValue_;
925  std::string commentsBefore_;
926  int stackDepth_;
927 
928  OurFeatures const features_;
929  bool collectComments_;
930 }; // OurReader
931 
932 // complete copy of Read impl, for OurReader
933 
934 OurReader::OurReader(OurFeatures const& features)
935  : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
936  lastValue_(), commentsBefore_(), features_(features), collectComments_() {
937 }
938 
939 bool OurReader::parse(const char* beginDoc,
940  const char* endDoc,
941  Value& root,
942  bool collectComments) {
943  if (!features_.allowComments_) {
944  collectComments = false;
945  }
946 
947  begin_ = beginDoc;
948  end_ = endDoc;
949  collectComments_ = collectComments;
950  current_ = begin_;
951  lastValueEnd_ = 0;
952  lastValue_ = 0;
953  commentsBefore_ = "";
954  errors_.clear();
955  while (!nodes_.empty())
956  nodes_.pop();
957  nodes_.push(&root);
958 
959  stackDepth_ = 0;
960  bool successful = readValue();
961  Token token;
962  skipCommentTokens(token);
963  if (features_.failIfExtra_) {
964  if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {
965  addError("Extra non-whitespace after JSON value.", token);
966  return false;
967  }
968  }
969  if (collectComments_ && !commentsBefore_.empty())
970  root.setComment(commentsBefore_, commentAfter);
971  if (features_.strictRoot_) {
972  if (!root.isArray() && !root.isObject()) {
973  // Set error location to start of doc, ideally should be first token found
974  // in doc
975  token.type_ = tokenError;
976  token.start_ = beginDoc;
977  token.end_ = endDoc;
978  addError(
979  "A valid JSON document must be either an array or an object value.",
980  token);
981  return false;
982  }
983  }
984  return successful;
985 }
986 
987 bool OurReader::readValue() {
988  if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
989  ++stackDepth_;
990  Token token;
991  skipCommentTokens(token);
992  bool successful = true;
993 
994  if (collectComments_ && !commentsBefore_.empty()) {
995  currentValue().setComment(commentsBefore_, commentBefore);
996  commentsBefore_ = "";
997  }
998 
999  switch (token.type_) {
1000  case tokenObjectBegin:
1001  successful = readObject(token);
1002  break;
1003  case tokenArrayBegin:
1004  successful = readArray(token);
1005  break;
1006  case tokenNumber:
1007  successful = decodeNumber(token);
1008  break;
1009  case tokenString:
1010  successful = decodeString(token);
1011  break;
1012  case tokenTrue:
1013  {
1014  Value v(true);
1015  currentValue().swapPayload(v);
1016  }
1017  break;
1018  case tokenFalse:
1019  {
1020  Value v(false);
1021  currentValue().swapPayload(v);
1022  }
1023  break;
1024  case tokenNull:
1025  {
1026  Value v;
1027  currentValue().swapPayload(v);
1028  }
1029  break;
1030  case tokenArraySeparator:
1031  case tokenObjectEnd:
1032  case tokenArrayEnd:
1033  if (features_.allowDroppedNullPlaceholders_) {
1034  // "Un-read" the current token and mark the current value as a null
1035  // token.
1036  current_--;
1037  Value v;
1038  currentValue().swapPayload(v);
1039  break;
1040  } // else, fall through ...
1041  default:
1042  return addError("Syntax error: value, object or array expected.", token);
1043  }
1044 
1045  if (collectComments_) {
1046  lastValueEnd_ = current_;
1047  lastValue_ = &currentValue();
1048  }
1049 
1050  --stackDepth_;
1051  return successful;
1052 }
1053 
1054 void OurReader::skipCommentTokens(Token& token) {
1055  if (features_.allowComments_) {
1056  do {
1057  readToken(token);
1058  } while (token.type_ == tokenComment);
1059  } else {
1060  readToken(token);
1061  }
1062 }
1063 
1064 bool OurReader::readToken(Token& token) {
1065  skipSpaces();
1066  token.start_ = current_;
1067  Char c = getNextChar();
1068  bool ok = true;
1069  switch (c) {
1070  case '{':
1071  token.type_ = tokenObjectBegin;
1072  break;
1073  case '}':
1074  token.type_ = tokenObjectEnd;
1075  break;
1076  case '[':
1077  token.type_ = tokenArrayBegin;
1078  break;
1079  case ']':
1080  token.type_ = tokenArrayEnd;
1081  break;
1082  case '"':
1083  token.type_ = tokenString;
1084  ok = readString();
1085  break;
1086  case '\'':
1087  if (features_.allowSingleQuotes_) {
1088  token.type_ = tokenString;
1089  ok = readStringSingleQuote();
1090  break;
1091  } // else continue
1092  case '/':
1093  token.type_ = tokenComment;
1094  ok = readComment();
1095  break;
1096  case '0':
1097  case '1':
1098  case '2':
1099  case '3':
1100  case '4':
1101  case '5':
1102  case '6':
1103  case '7':
1104  case '8':
1105  case '9':
1106  case '-':
1107  token.type_ = tokenNumber;
1108  readNumber();
1109  break;
1110  case 't':
1111  token.type_ = tokenTrue;
1112  ok = match("rue", 3);
1113  break;
1114  case 'f':
1115  token.type_ = tokenFalse;
1116  ok = match("alse", 4);
1117  break;
1118  case 'n':
1119  token.type_ = tokenNull;
1120  ok = match("ull", 3);
1121  break;
1122  case ',':
1123  token.type_ = tokenArraySeparator;
1124  break;
1125  case ':':
1126  token.type_ = tokenMemberSeparator;
1127  break;
1128  case 0:
1129  token.type_ = tokenEndOfStream;
1130  break;
1131  default:
1132  ok = false;
1133  break;
1134  }
1135  if (!ok)
1136  token.type_ = tokenError;
1137  token.end_ = current_;
1138  return true;
1139 }
1140 
1141 void OurReader::skipSpaces() {
1142  while (current_ != end_) {
1143  Char c = *current_;
1144  if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1145  ++current_;
1146  else
1147  break;
1148  }
1149 }
1150 
1151 bool OurReader::match(Location pattern, int patternLength) {
1152  if (end_ - current_ < patternLength)
1153  return false;
1154  int index = patternLength;
1155  while (index--)
1156  if (current_[index] != pattern[index])
1157  return false;
1158  current_ += patternLength;
1159  return true;
1160 }
1161 
1162 bool OurReader::readComment() {
1163  Location commentBegin = current_ - 1;
1164  Char c = getNextChar();
1165  bool successful = false;
1166  if (c == '*')
1167  successful = readCStyleComment();
1168  else if (c == '/')
1169  successful = readCppStyleComment();
1170  if (!successful)
1171  return false;
1172 
1173  if (collectComments_) {
1174  CommentPlacement placement = commentBefore;
1175  if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1176  if (c != '*' || !containsNewLine(commentBegin, current_))
1177  placement = commentAfterOnSameLine;
1178  }
1179 
1180  addComment(commentBegin, current_, placement);
1181  }
1182  return true;
1183 }
1184 
1185 void
1186 OurReader::addComment(Location begin, Location end, CommentPlacement placement) {
1187  assert(collectComments_);
1188  const std::string& normalized = normalizeEOL(begin, end);
1189  if (placement == commentAfterOnSameLine) {
1190  assert(lastValue_ != 0);
1191  lastValue_->setComment(normalized, placement);
1192  } else {
1193  commentsBefore_ += normalized;
1194  }
1195 }
1196 
1197 bool OurReader::readCStyleComment() {
1198  while (current_ != end_) {
1199  Char c = getNextChar();
1200  if (c == '*' && *current_ == '/')
1201  break;
1202  }
1203  return getNextChar() == '/';
1204 }
1205 
1206 bool OurReader::readCppStyleComment() {
1207  while (current_ != end_) {
1208  Char c = getNextChar();
1209  if (c == '\n')
1210  break;
1211  if (c == '\r') {
1212  // Consume DOS EOL. It will be normalized in addComment.
1213  if (current_ != end_ && *current_ == '\n')
1214  getNextChar();
1215  // Break on Moc OS 9 EOL.
1216  break;
1217  }
1218  }
1219  return true;
1220 }
1221 
1222 void OurReader::readNumber() {
1223  const char *p = current_;
1224  char c = '0'; // stopgap for already consumed character
1225  // integral part
1226  while (c >= '0' && c <= '9')
1227  c = (current_ = p) < end_ ? *p++ : 0;
1228  // fractional part
1229  if (c == '.') {
1230  c = (current_ = p) < end_ ? *p++ : 0;
1231  while (c >= '0' && c <= '9')
1232  c = (current_ = p) < end_ ? *p++ : 0;
1233  }
1234  // exponential part
1235  if (c == 'e' || c == 'E') {
1236  c = (current_ = p) < end_ ? *p++ : 0;
1237  if (c == '+' || c == '-')
1238  c = (current_ = p) < end_ ? *p++ : 0;
1239  while (c >= '0' && c <= '9')
1240  c = (current_ = p) < end_ ? *p++ : 0;
1241  }
1242 }
1243 bool OurReader::readString() {
1244  Char c = 0;
1245  while (current_ != end_) {
1246  c = getNextChar();
1247  if (c == '\\')
1248  getNextChar();
1249  else if (c == '"')
1250  break;
1251  }
1252  return c == '"';
1253 }
1254 
1255 
1256 bool OurReader::readStringSingleQuote() {
1257  Char c = 0;
1258  while (current_ != end_) {
1259  c = getNextChar();
1260  if (c == '\\')
1261  getNextChar();
1262  else if (c == '\'')
1263  break;
1264  }
1265  return c == '\'';
1266 }
1267 
1268 bool OurReader::readObject(Token& /*tokenStart*/) {
1269  Token tokenName;
1270  std::string name;
1271  Value init(objectValue);
1272  currentValue().swapPayload(init);
1273  while (readToken(tokenName)) {
1274  bool initialTokenOk = true;
1275  while (tokenName.type_ == tokenComment && initialTokenOk)
1276  initialTokenOk = readToken(tokenName);
1277  if (!initialTokenOk)
1278  break;
1279  if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
1280  return true;
1281  name = "";
1282  if (tokenName.type_ == tokenString) {
1283  if (!decodeString(tokenName, name))
1284  return recoverFromError(tokenObjectEnd);
1285  } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1286  Value numberName;
1287  if (!decodeNumber(tokenName, numberName))
1288  return recoverFromError(tokenObjectEnd);
1289  name = numberName.asString();
1290  } else {
1291  break;
1292  }
1293 
1294  Token colon;
1295  if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1296  return addErrorAndRecover(
1297  "Missing ':' after object member name", colon, tokenObjectEnd);
1298  }
1299  if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
1300  if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1301  std::string msg = "Duplicate key: '" + name + "'";
1302  return addErrorAndRecover(
1303  msg, tokenName, tokenObjectEnd);
1304  }
1305  Value& value = currentValue()[name];
1306  nodes_.push(&value);
1307  bool ok = readValue();
1308  nodes_.pop();
1309  if (!ok) // error already set
1310  return recoverFromError(tokenObjectEnd);
1311 
1312  Token comma;
1313  if (!readToken(comma) ||
1314  (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
1315  comma.type_ != tokenComment)) {
1316  return addErrorAndRecover(
1317  "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
1318  }
1319  bool finalizeTokenOk = true;
1320  while (comma.type_ == tokenComment && finalizeTokenOk)
1321  finalizeTokenOk = readToken(comma);
1322  if (comma.type_ == tokenObjectEnd)
1323  return true;
1324  }
1325  return addErrorAndRecover(
1326  "Missing '}' or object member name", tokenName, tokenObjectEnd);
1327 }
1328 
1329 bool OurReader::readArray(Token& /*tokenStart*/) {
1330  Value init(arrayValue);
1331  currentValue().swapPayload(init);
1332  skipSpaces();
1333  if (*current_ == ']') // empty array
1334  {
1335  Token endArray;
1336  readToken(endArray);
1337  return true;
1338  }
1339  int index = 0;
1340  for (;;) {
1341  Value& value = currentValue()[index++];
1342  nodes_.push(&value);
1343  bool ok = readValue();
1344  nodes_.pop();
1345  if (!ok) // error already set
1346  return recoverFromError(tokenArrayEnd);
1347 
1348  Token token;
1349  // Accept Comment after last item in the array.
1350  ok = readToken(token);
1351  while (token.type_ == tokenComment && ok) {
1352  ok = readToken(token);
1353  }
1354  bool badTokenType =
1355  (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
1356  if (!ok || badTokenType) {
1357  return addErrorAndRecover(
1358  "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
1359  }
1360  if (token.type_ == tokenArrayEnd)
1361  break;
1362  }
1363  return true;
1364 }
1365 
1366 bool OurReader::decodeNumber(Token& token) {
1367  Value decoded;
1368  if (!decodeNumber(token, decoded))
1369  return false;
1370  currentValue().swapPayload(decoded);
1371  return true;
1372 }
1373 
1374 bool OurReader::decodeNumber(Token& token, Value& decoded) {
1375  // Attempts to parse the number as an integer. If the number is
1376  // larger than the maximum supported value of an integer then
1377  // we decode the number as a double.
1378  Location current = token.start_;
1379  bool isNegative = *current == '-';
1380  if (isNegative)
1381  ++current;
1382  // TODO: Help the compiler do the div and mod at compile time or get rid of them.
1383  Value::LargestUInt maxIntegerValue =
1385  : Value::maxLargestUInt;
1386  Value::LargestUInt threshold = maxIntegerValue / 10;
1387  Value::LargestUInt value = 0;
1388  while (current < token.end_) {
1389  Char c = *current++;
1390  if (c < '0' || c > '9')
1391  return decodeDouble(token, decoded);
1392  Value::UInt digit(c - '0');
1393  if (value >= threshold) {
1394  // We've hit or exceeded the max value divided by 10 (rounded down). If
1395  // a) we've only just touched the limit, b) this is the last digit, and
1396  // c) it's small enough to fit in that rounding delta, we're okay.
1397  // Otherwise treat this number as a double to avoid overflow.
1398  if (value > threshold || current != token.end_ ||
1399  digit > maxIntegerValue % 10) {
1400  return decodeDouble(token, decoded);
1401  }
1402  }
1403  value = value * 10 + digit;
1404  }
1405  if (isNegative)
1406  decoded = -Value::LargestInt(value);
1407  else if (value <= Value::LargestUInt(Value::maxInt))
1408  decoded = Value::LargestInt(value);
1409  else
1410  decoded = value;
1411  return true;
1412 }
1413 
1414 bool OurReader::decodeDouble(Token& token) {
1415  Value decoded;
1416  if (!decodeDouble(token, decoded))
1417  return false;
1418  currentValue().swapPayload(decoded);
1419  return true;
1420 }
1421 
1422 bool OurReader::decodeDouble(Token& token, Value& decoded) {
1423  double value = 0;
1424  const int bufferSize = 32;
1425  int count;
1426  int length = int(token.end_ - token.start_);
1427 
1428  // Sanity check to avoid buffer overflow exploits.
1429  if (length < 0) {
1430  return addError("Unable to parse token length", token);
1431  }
1432 
1433  // Avoid using a string constant for the format control string given to
1434  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
1435  // info:
1436  //
1437  // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
1438  char format[] = "%lf";
1439 
1440  if (length <= bufferSize) {
1441  Char buffer[bufferSize + 1];
1442  memcpy(buffer, token.start_, length);
1443  buffer[length] = 0;
1444  count = sscanf(buffer, format, &value);
1445  } else {
1446  std::string buffer(token.start_, token.end_);
1447  count = sscanf(buffer.c_str(), format, &value);
1448  }
1449 
1450  if (count != 1)
1451  return addError("'" + std::string(token.start_, token.end_) +
1452  "' is not a number.",
1453  token);
1454  decoded = value;
1455  return true;
1456 }
1457 
1458 bool OurReader::decodeString(Token& token) {
1459  std::string decoded_string;
1460  if (!decodeString(token, decoded_string))
1461  return false;
1462  Value decoded(decoded_string);
1463  currentValue().swapPayload(decoded);
1464  return true;
1465 }
1466 
1467 bool OurReader::decodeString(Token& token, std::string& decoded) {
1468  decoded.reserve(token.end_ - token.start_ - 2);
1469  Location current = token.start_ + 1; // skip '"'
1470  Location end = token.end_ - 1; // do not include '"'
1471  while (current != end) {
1472  Char c = *current++;
1473  if (c == '"')
1474  break;
1475  else if (c == '\\') {
1476  if (current == end)
1477  return addError("Empty escape sequence in string", token, current);
1478  Char escape = *current++;
1479  switch (escape) {
1480  case '"':
1481  decoded += '"';
1482  break;
1483  case '/':
1484  decoded += '/';
1485  break;
1486  case '\\':
1487  decoded += '\\';
1488  break;
1489  case 'b':
1490  decoded += '\b';
1491  break;
1492  case 'f':
1493  decoded += '\f';
1494  break;
1495  case 'n':
1496  decoded += '\n';
1497  break;
1498  case 'r':
1499  decoded += '\r';
1500  break;
1501  case 't':
1502  decoded += '\t';
1503  break;
1504  case 'u': {
1505  unsigned int unicode;
1506  if (!decodeUnicodeCodePoint(token, current, end, unicode))
1507  return false;
1508  decoded += codePointToUTF8(unicode);
1509  } break;
1510  default:
1511  return addError("Bad escape sequence in string", token, current);
1512  }
1513  } else {
1514  decoded += c;
1515  }
1516  }
1517  return true;
1518 }
1519 
1520 bool OurReader::decodeUnicodeCodePoint(Token& token,
1521  Location& current,
1522  Location end,
1523  unsigned int& unicode) {
1524 
1525  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1526  return false;
1527  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1528  // surrogate pairs
1529  if (end - current < 6)
1530  return addError(
1531  "additional six characters expected to parse unicode surrogate pair.",
1532  token,
1533  current);
1534  unsigned int surrogatePair;
1535  if (*(current++) == '\\' && *(current++) == 'u') {
1536  if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1537  unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1538  } else
1539  return false;
1540  } else
1541  return addError("expecting another \\u token to begin the second half of "
1542  "a unicode surrogate pair",
1543  token,
1544  current);
1545  }
1546  return true;
1547 }
1548 
1549 bool OurReader::decodeUnicodeEscapeSequence(Token& token,
1550  Location& current,
1551  Location end,
1552  unsigned int& unicode) {
1553  if (end - current < 4)
1554  return addError(
1555  "Bad unicode escape sequence in string: four digits expected.",
1556  token,
1557  current);
1558  unicode = 0;
1559  for (int index = 0; index < 4; ++index) {
1560  Char c = *current++;
1561  unicode *= 16;
1562  if (c >= '0' && c <= '9')
1563  unicode += c - '0';
1564  else if (c >= 'a' && c <= 'f')
1565  unicode += c - 'a' + 10;
1566  else if (c >= 'A' && c <= 'F')
1567  unicode += c - 'A' + 10;
1568  else
1569  return addError(
1570  "Bad unicode escape sequence in string: hexadecimal digit expected.",
1571  token,
1572  current);
1573  }
1574  return true;
1575 }
1576 
1577 bool
1578 OurReader::addError(const std::string& message, Token& token, Location extra) {
1579  ErrorInfo info;
1580  info.token_ = token;
1581  info.message_ = message;
1582  info.extra_ = extra;
1583  errors_.push_back(info);
1584  return false;
1585 }
1586 
1587 bool OurReader::recoverFromError(TokenType skipUntilToken) {
1588  int errorCount = int(errors_.size());
1589  Token skip;
1590  for (;;) {
1591  if (!readToken(skip))
1592  errors_.resize(errorCount); // discard errors caused by recovery
1593  if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1594  break;
1595  }
1596  errors_.resize(errorCount);
1597  return false;
1598 }
1599 
1600 bool OurReader::addErrorAndRecover(const std::string& message,
1601  Token& token,
1602  TokenType skipUntilToken) {
1603  addError(message, token);
1604  return recoverFromError(skipUntilToken);
1605 }
1606 
1607 Value& OurReader::currentValue() { return *(nodes_.top()); }
1608 
1609 OurReader::Char OurReader::getNextChar() {
1610  if (current_ == end_)
1611  return 0;
1612  return *current_++;
1613 }
1614 
1615 void OurReader::getLocationLineAndColumn(Location location,
1616  int& line,
1617  int& column) const {
1618  Location current = begin_;
1619  Location lastLineStart = current;
1620  line = 0;
1621  while (current < location && current != end_) {
1622  Char c = *current++;
1623  if (c == '\r') {
1624  if (*current == '\n')
1625  ++current;
1626  lastLineStart = current;
1627  ++line;
1628  } else if (c == '\n') {
1629  lastLineStart = current;
1630  ++line;
1631  }
1632  }
1633  // column & line start at 1
1634  column = int(location - lastLineStart) + 1;
1635  ++line;
1636 }
1637 
1638 std::string OurReader::getLocationLineAndColumn(Location location) const {
1639  int line, column;
1640  getLocationLineAndColumn(location, line, column);
1641  char buffer[18 + 16 + 16 + 1];
1642 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
1643 #if defined(WINCE)
1644  _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1645 #else
1646  sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1647 #endif
1648 #else
1649  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
1650 #endif
1651  return buffer;
1652 }
1653 
1654 std::string OurReader::getFormattedErrorMessages() const {
1655  std::string formattedMessage;
1656  for (Errors::const_iterator itError = errors_.begin();
1657  itError != errors_.end();
1658  ++itError) {
1659  const ErrorInfo& error = *itError;
1660  formattedMessage +=
1661  "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
1662  formattedMessage += " " + error.message_ + "\n";
1663  if (error.extra_)
1664  formattedMessage +=
1665  "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
1666  }
1667  return formattedMessage;
1668 }
1669 
1670 
1671 class OurCharReader : public CharReader {
1672  bool const collectComments_;
1673  OurReader reader_;
1674 public:
1675  OurCharReader(
1676  bool collectComments,
1677  OurFeatures const& features)
1678  : collectComments_(collectComments)
1679  , reader_(features)
1680  {}
1681  virtual bool parse(
1682  char const* beginDoc, char const* endDoc,
1683  Value* root, std::string* errs) {
1684  bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1685  if (errs) {
1686  *errs = reader_.getFormattedErrorMessages();
1687  }
1688  return ok;
1689  }
1690 };
1691 
1693 {
1695 }
1697 {}
1699 {
1700  bool collectComments = settings_["collectComments"].asBool();
1701  OurFeatures features = OurFeatures::all();
1702  features.allowComments_ = settings_["allowComments"].asBool();
1703  features.strictRoot_ = settings_["strictRoot"].asBool();
1704  features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
1705  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
1706  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
1707  features.stackLimit_ = settings_["stackLimit"].asInt();
1708  features.failIfExtra_ = settings_["failIfExtra"].asBool();
1709  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
1710  return new OurCharReader(collectComments, features);
1711 }
1712 static void getValidReaderKeys(std::set<std::string>* valid_keys)
1713 {
1714  valid_keys->clear();
1715  valid_keys->insert("collectComments");
1716  valid_keys->insert("allowComments");
1717  valid_keys->insert("strictRoot");
1718  valid_keys->insert("allowDroppedNullPlaceholders");
1719  valid_keys->insert("allowNumericKeys");
1720  valid_keys->insert("allowSingleQuotes");
1721  valid_keys->insert("stackLimit");
1722  valid_keys->insert("failIfExtra");
1723  valid_keys->insert("rejectDupKeys");
1724 }
1726 {
1727  Json::Value my_invalid;
1728  if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
1729  Json::Value& inv = *invalid;
1730  std::set<std::string> valid_keys;
1731  getValidReaderKeys(&valid_keys);
1733  size_t n = keys.size();
1734  for (size_t i = 0; i < n; ++i) {
1735  std::string const& key = keys[i];
1736  if (valid_keys.find(key) == valid_keys.end()) {
1737  inv[key] = settings_[key];
1738  }
1739  }
1740  return 0u == inv.size();
1741 }
1743 {
1744  return settings_[key];
1745 }
1746 // static
1748 {
1750  (*settings)["allowComments"] = false;
1751  (*settings)["strictRoot"] = true;
1752  (*settings)["allowDroppedNullPlaceholders"] = false;
1753  (*settings)["allowNumericKeys"] = false;
1754  (*settings)["allowSingleQuotes"] = false;
1755  (*settings)["failIfExtra"] = true;
1756  (*settings)["rejectDupKeys"] = true;
1758 }
1759 // static
1761 {
1763  (*settings)["collectComments"] = true;
1764  (*settings)["allowComments"] = true;
1765  (*settings)["strictRoot"] = false;
1766  (*settings)["allowDroppedNullPlaceholders"] = false;
1767  (*settings)["allowNumericKeys"] = false;
1768  (*settings)["allowSingleQuotes"] = false;
1769  (*settings)["stackLimit"] = 1000;
1770  (*settings)["failIfExtra"] = false;
1771  (*settings)["rejectDupKeys"] = false;
1773 }
1774 
1776 // global functions
1777 
1779  CharReader::Factory const& fact, std::istream& sin,
1780  Value* root, std::string* errs)
1781 {
1782  std::ostringstream ssin;
1783  ssin << sin.rdbuf();
1784  std::string doc = ssin.str();
1785  char const* begin = doc.data();
1786  char const* end = begin + doc.size();
1787  // Note that we do not actually need a null-terminator.
1788  CharReaderPtr const reader(fact.newCharReader());
1789  return reader->parse(begin, end, root, errs);
1790 }
1791 
1792 std::istream& operator>>(std::istream& sin, Value& root) {
1794  std::string errs;
1795  bool ok = parseFromStream(b, sin, &root, &errs);
1796  if (!ok) {
1797  fprintf(stderr,
1798  "Error from reader: %s",
1799  errs.c_str());
1800 
1801  throwRuntimeError("reader error");
1802  }
1803  return sin;
1804 }
1805 
1806 } // namespace Json
static std::string codePointToUTF8(unsigned int cp)
Converts a unicode code-point to UTF-8.
Definition: json_tool.h:18
Int asInt() const
Definition: json_value.cpp:636
std::vector< std::string > Members
Definition: value.h:165
virtual CharReader * newCharReader() const
Allocate a CharReader via operator new().
static void strictMode(Json::Value *settings)
Same as old Features::strictMode().
array value (ordered list)
Definition: value.h:85
std::auto_ptr< CharReader > CharReaderPtr
Definition: json_reader.cpp:35
bool parseFromStream(CharReader::Factory const &, std::istream &, Value *root, std::string *errs)
Consume entire stream and use its begin/end.
Json::Value settings_
Configuration of this builder.
Definition: reader.h:286
object value (collection of name/value pairs).
Definition: value.h:86
std::istream & operator>>(std::istream &, Value &)
Read from &#39;sin&#39; into &#39;root&#39;.
char Char
Definition: reader.h:35
std::string getFormatedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
void swapPayload(Value &other)
Swap values but leave comments and source offsets in place.
Definition: json_value.cpp:459
Value & operator[](std::string key)
A simple way to update a specific setting.
static const Int maxInt
Maximum signed int value that can be stored in a Json::Value.
Definition: value.h:193
Json::LargestUInt LargestUInt
Definition: value.h:175
Features()
Initialize the configuration like JsonConfig::allFeatures;.
Definition: json_reader.cpp:40
bool asBool() const
Definition: json_value.cpp:785
bool isObject() const
void setComment(const char *comment, CommentPlacement placement)
static const LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
Definition: value.h:184
bool allowComments_
true if comments are allowed. Default: true.
Definition: features.h:42
CommentPlacement
Definition: value.h:89
const Char * Location
Definition: reader.h:36
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
Definition: json_reader.cpp:76
Members getMemberNames() const
Return a list of the member names.
bool validate(Json::Value *invalid) const
Json::LargestInt LargestInt
Definition: value.h:174
static int const stackLimit_g
Definition: json_reader.cpp:30
void throwRuntimeError(std::string const &msg)
used internally
Definition: json_value.cpp:171
Json::UInt UInt
Definition: value.h:168
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
Interface for reading JSON from a char array.
Definition: reader.h:203
ArrayIndex size() const
Number of values in array or object.
Definition: json_value.cpp:838
Represents a JSON value.
Definition: value.h:162
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
Definition: json_reader.cpp:43
static std::string normalizeEOL(Reader::Location begin, Reader::Location end)
a comment on the line after a value (only make sense for
Definition: value.h:92
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
Definition: json_reader.cpp:45
bool strictRoot_
true if root must be either an array or an object value.
Definition: features.h:46
bool isArray() const
Build a CharReader implementation.
Definition: reader.h:249
static int stackDepth_g
Definition: json_reader.cpp:31
#define snprintf
Definition: json_reader.cpp:22
static void getValidReaderKeys(std::set< std::string > *valid_keys)
static bool containsNewLine(Reader::Location begin, Reader::Location end)
Definition: json_reader.cpp:55
Configuration passed to reader and writer.
Definition: features.h:19
virtual CharReader * newCharReader() const =0
Allocate a CharReader via operator new().
a comment placed on the line before a value
Definition: value.h:90
Reader()
Constructs a Reader allowing all features for parsing.
Definition: json_reader.cpp:65
std::string getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
a comment just after a value on the same line
Definition: value.h:91