Upgrade jsoncpp to 1.9.4

Bug: 170642246
Change-Id: Id1fae5a1b6421117f923c616718ee4b3571231e0
diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp
index 6136230..19922a8 100644
--- a/src/lib_json/json_reader.cpp
+++ b/src/lib_json/json_reader.cpp
@@ -1,41 +1,69 @@
-// Copyright 2007-2011 Baptiste Lepilleur
+// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
+// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
 // Distributed under MIT license, or public domain if desired and
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
 #if !defined(JSON_IS_AMALGAMATION)
+#include "json_tool.h"
 #include <json/assertions.h>
 #include <json/reader.h>
 #include <json/value.h>
-#include "json_tool.h"
 #endif // if !defined(JSON_IS_AMALGAMATION)
-#include <iostream>
-#include <fstream>
-#include <utility>
-#include <cstdio>
+#include <algorithm>
 #include <cassert>
 #include <cstring>
+#include <iostream>
 #include <istream>
+#include <limits>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
 
-#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
-#define snprintf _snprintf
+#include <cstdio>
+#if __cplusplus >= 201103L
+
+#if !defined(sscanf)
+#define sscanf std::sscanf
 #endif
 
-#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+#endif //__cplusplus
+
+#if defined(_MSC_VER)
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
+#endif //_MSC_VER
+
+#if defined(_MSC_VER)
 // Disable warning about strdup being deprecated.
 #pragma warning(disable : 4996)
 #endif
 
+// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
+// time to change the stack limit
+#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
+#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
+#endif
+
+static size_t const stackLimit_g =
+    JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
+
 namespace Json {
 
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+using CharReaderPtr = std::unique_ptr<CharReader>;
+#else
+using CharReaderPtr = std::auto_ptr<CharReader>;
+#endif
+
 // Implementation of class Features
 // ////////////////////////////////
 
-Features::Features()
-    : allowComments_(true), strictRoot_(false),
-      allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
+Features::Features() = default;
 
-Features Features::all() { return Features(); }
+Features Features::all() { return {}; }
 
 Features Features::strictMode() {
   Features features;
@@ -49,67 +77,39 @@
 // Implementation of class Reader
 // ////////////////////////////////
 
-static inline bool in(Reader::Char c,
-                      Reader::Char c1,
-                      Reader::Char c2,
-                      Reader::Char c3,
-                      Reader::Char c4) {
-  return c == c1 || c == c2 || c == c3 || c == c4;
-}
-
-static inline bool in(Reader::Char c,
-                      Reader::Char c1,
-                      Reader::Char c2,
-                      Reader::Char c3,
-                      Reader::Char c4,
-                      Reader::Char c5) {
-  return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
-}
-
-static bool containsNewLine(Reader::Location begin, Reader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
+bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
+  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
 }
 
 // Class Reader
 // //////////////////////////////////////////////////////////////////
 
-Reader::Reader()
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(), features_(Features::all()),
-      collectComments_() {}
+Reader::Reader() : features_(Features::all()) {}
 
-Reader::Reader(const Features& features)
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
-}
+Reader::Reader(const Features& features) : features_(features) {}
 
-bool
-Reader::parse(const std::string& document, Value& root, bool collectComments) {
-  document_ = document;
+bool Reader::parse(const std::string& document, Value& root,
+                   bool collectComments) {
+  document_.assign(document.begin(), document.end());
   const char* begin = document_.c_str();
   const char* end = begin + document_.length();
   return parse(begin, end, root, collectComments);
 }
 
-bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
-  // std::istream_iterator<char> begin(sin);
+bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
+  // std::istream_iterator<char> begin(is);
   // std::istream_iterator<char> end;
   // Those would allow streamed input from a file, if parse() were a
   // template function.
 
-  // Since std::string is reference-counted, this at least does not
+  // Since String is reference-counted, this at least does not
   // create an extra copy.
-  std::string doc;
-  std::getline(sin, doc, (char)EOF);
-  return parse(doc, root, collectComments);
+  String doc;
+  std::getline(is, doc, static_cast<char> EOF);
+  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
 }
 
-bool Reader::parse(const char* beginDoc,
-                   const char* endDoc,
-                   Value& root,
+bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
                    bool collectComments) {
   if (!features_.allowComments_) {
     collectComments = false;
@@ -119,9 +119,9 @@
   end_ = endDoc;
   collectComments_ = collectComments;
   current_ = begin_;
-  lastValueEnd_ = 0;
-  lastValue_ = 0;
-  commentsBefore_ = "";
+  lastValueEnd_ = nullptr;
+  lastValue_ = nullptr;
+  commentsBefore_.clear();
   errors_.clear();
   while (!nodes_.empty())
     nodes_.pop();
@@ -149,21 +149,20 @@
 }
 
 bool Reader::readValue() {
+  // readValue() may call itself only if it calls readObject() or ReadArray().
+  // These methods execute nodes_.push() just before and nodes_.pop)() just
+  // after calling readValue(). parse() executes one nodes_.push(), so > instead
+  // of >=.
+  if (nodes_.size() > stackLimit_g)
+    throwRuntimeError("Exceeded stackLimit in readValue().");
+
   Token token;
   skipCommentTokens(token);
   bool successful = true;
 
   if (collectComments_ && !commentsBefore_.empty()) {
-    // Remove newline characters at the end of the comments
-    size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n");
-    if (lastNonNewline != std::string::npos) {
-      commentsBefore_.erase(lastNonNewline + 1);
-    } else {
-      commentsBefore_.clear();
-    }
-
     currentValue().setComment(commentsBefore_, commentBefore);
-    commentsBefore_ = "";
+    commentsBefore_.clear();
   }
 
   switch (token.type_) {
@@ -181,32 +180,37 @@
   case tokenString:
     successful = decodeString(token);
     break;
-  case tokenTrue:
-    currentValue() = true;
+  case tokenTrue: {
+    Value v(true);
+    currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    break;
-  case tokenFalse:
-    currentValue() = false;
+  } break;
+  case tokenFalse: {
+    Value v(false);
+    currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    break;
-  case tokenNull:
-    currentValue() = Value();
+  } break;
+  case tokenNull: {
+    Value v;
+    currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    break;
+  } break;
   case tokenArraySeparator:
+  case tokenObjectEnd:
+  case tokenArrayEnd:
     if (features_.allowDroppedNullPlaceholders_) {
       // "Un-read" the current token and mark the current value as a null
       // token.
       current_--;
-      currentValue() = Value();
+      Value v;
+      currentValue().swapPayload(v);
       currentValue().setOffsetStart(current_ - begin_ - 1);
       currentValue().setOffsetLimit(current_ - begin_);
       break;
-    }
-  // Else, fall through...
+    } // Else, fall through...
   default:
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
@@ -231,13 +235,6 @@
   }
 }
 
-bool Reader::expectToken(TokenType type, Token& token, const char* message) {
-  readToken(token);
-  if (token.type_ != type)
-    return addError(message, token);
-  return true;
-}
-
 bool Reader::readToken(Token& token) {
   skipSpaces();
   token.start_ = current_;
@@ -306,7 +303,7 @@
   if (!ok)
     token.type_ = tokenError;
   token.end_ = current_;
-  return true;
+  return ok;
 }
 
 void Reader::skipSpaces() {
@@ -319,7 +316,7 @@
   }
 }
 
-bool Reader::match(Location pattern, int patternLength) {
+bool Reader::match(const Char* pattern, int patternLength) {
   if (end_ - current_ < patternLength)
     return false;
   int index = patternLength;
@@ -353,19 +350,39 @@
   return true;
 }
 
-void
-Reader::addComment(Location begin, Location end, CommentPlacement placement) {
+String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
+  String normalized;
+  normalized.reserve(static_cast<size_t>(end - begin));
+  Reader::Location current = begin;
+  while (current != end) {
+    char c = *current++;
+    if (c == '\r') {
+      if (current != end && *current == '\n')
+        // convert dos EOL
+        ++current;
+      // convert Mac EOL
+      normalized += '\n';
+    } else {
+      normalized += c;
+    }
+  }
+  return normalized;
+}
+
+void Reader::addComment(Location begin, Location end,
+                        CommentPlacement placement) {
   assert(collectComments_);
+  const String& normalized = normalizeEOL(begin, end);
   if (placement == commentAfterOnSameLine) {
-    assert(lastValue_ != 0);
-    lastValue_->setComment(std::string(begin, end), placement);
+    assert(lastValue_ != nullptr);
+    lastValue_->setComment(normalized, placement);
   } else {
-    commentsBefore_ += std::string(begin, end);
+    commentsBefore_ += normalized;
   }
 }
 
 bool Reader::readCStyleComment() {
-  while (current_ != end_) {
+  while ((current_ + 1) < end_) {
     Char c = getNextChar();
     if (c == '*' && *current_ == '/')
       break;
@@ -376,23 +393,43 @@
 bool Reader::readCppStyleComment() {
   while (current_ != end_) {
     Char c = getNextChar();
-    if (c == '\r' || c == '\n')
+    if (c == '\n')
       break;
+    if (c == '\r') {
+      // Consume DOS EOL. It will be normalized in addComment.
+      if (current_ != end_ && *current_ == '\n')
+        getNextChar();
+      // Break on Moc OS 9 EOL.
+      break;
+    }
   }
   return true;
 }
 
 void Reader::readNumber() {
-  while (current_ != end_) {
-    if (!(*current_ >= '0' && *current_ <= '9') &&
-        !in(*current_, '.', 'e', 'E', '+', '-'))
-      break;
-    ++current_;
+  Location p = current_;
+  char c = '0'; // stopgap for already consumed character
+  // integral part
+  while (c >= '0' && c <= '9')
+    c = (current_ = p) < end_ ? *p++ : '\0';
+  // fractional part
+  if (c == '.') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+  // exponential part
+  if (c == 'e' || c == 'E') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    if (c == '+' || c == '-')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
   }
 }
 
 bool Reader::readString() {
-  Char c = 0;
+  Char c = '\0';
   while (current_ != end_) {
     c = getNextChar();
     if (c == '\\')
@@ -403,11 +440,12 @@
   return c == '"';
 }
 
-bool Reader::readObject(Token& tokenStart) {
+bool Reader::readObject(Token& token) {
   Token tokenName;
-  std::string name;
-  currentValue() = Value(objectValue);
-  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  String name;
+  Value init(objectValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
   while (readToken(tokenName)) {
     bool initialTokenOk = true;
     while (tokenName.type_ == tokenComment && initialTokenOk)
@@ -416,7 +454,7 @@
       break;
     if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
       return true;
-    name = "";
+    name.clear();
     if (tokenName.type_ == tokenString) {
       if (!decodeString(tokenName, name))
         return recoverFromError(tokenObjectEnd);
@@ -431,8 +469,8 @@
 
     Token colon;
     if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
-      return addErrorAndRecover(
-          "Missing ':' after object member name", colon, tokenObjectEnd);
+      return addErrorAndRecover("Missing ':' after object member name", colon,
+                                tokenObjectEnd);
     }
     Value& value = currentValue()[name];
     nodes_.push(&value);
@@ -445,8 +483,8 @@
     if (!readToken(comma) ||
         (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
          comma.type_ != tokenComment)) {
-      return addErrorAndRecover(
-          "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+      return addErrorAndRecover("Missing ',' or '}' in object declaration",
+                                comma, tokenObjectEnd);
     }
     bool finalizeTokenOk = true;
     while (comma.type_ == tokenComment && finalizeTokenOk)
@@ -454,15 +492,16 @@
     if (comma.type_ == tokenObjectEnd)
       return true;
   }
-  return addErrorAndRecover(
-      "Missing '}' or object member name", tokenName, tokenObjectEnd);
+  return addErrorAndRecover("Missing '}' or object member name", tokenName,
+                            tokenObjectEnd);
 }
 
-bool Reader::readArray(Token& tokenStart) {
-  currentValue() = Value(arrayValue);
-  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+bool Reader::readArray(Token& token) {
+  Value init(arrayValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
   skipSpaces();
-  if (*current_ == ']') // empty array
+  if (current_ != end_ && *current_ == ']') // empty array
   {
     Token endArray;
     readToken(endArray);
@@ -477,19 +516,19 @@
     if (!ok) // error already set
       return recoverFromError(tokenArrayEnd);
 
-    Token token;
+    Token currentToken;
     // Accept Comment after last item in the array.
-    ok = readToken(token);
-    while (token.type_ == tokenComment && ok) {
-      ok = readToken(token);
+    ok = readToken(currentToken);
+    while (currentToken.type_ == tokenComment && ok) {
+      ok = readToken(currentToken);
     }
-    bool badTokenType =
-        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+                         currentToken.type_ != tokenArrayEnd);
     if (!ok || badTokenType) {
-      return addErrorAndRecover(
-          "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+      return addErrorAndRecover("Missing ',' or ']' in array declaration",
+                                currentToken, tokenArrayEnd);
     }
-    if (token.type_ == tokenArrayEnd)
+    if (currentToken.type_ == tokenArrayEnd)
       break;
   }
   return true;
@@ -499,20 +538,13 @@
   Value decoded;
   if (!decodeNumber(token, decoded))
     return false;
-  currentValue() = decoded;
+  currentValue().swapPayload(decoded);
   currentValue().setOffsetStart(token.start_ - begin_);
   currentValue().setOffsetLimit(token.end_ - begin_);
   return true;
 }
 
 bool Reader::decodeNumber(Token& token, Value& decoded) {
-  bool isDouble = false;
-  for (Location inspect = token.start_; inspect != token.end_; ++inspect) {
-    isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') ||
-               (*inspect == '-' && inspect != token.start_);
-  }
-  if (isDouble)
-    return decodeDouble(token, decoded);
   // Attempts to parse the number as an integer. If the number is
   // larger than the maximum supported value of an integer then
   // we decode the number as a double.
@@ -520,18 +552,18 @@
   bool isNegative = *current == '-';
   if (isNegative)
     ++current;
+  // TODO: Help the compiler do the div and mod at compile time or get rid of
+  // them.
   Value::LargestUInt maxIntegerValue =
-      isNegative ? Value::LargestUInt(-Value::minLargestInt)
+      isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
                  : Value::maxLargestUInt;
   Value::LargestUInt threshold = maxIntegerValue / 10;
   Value::LargestUInt value = 0;
   while (current < token.end_) {
     Char c = *current++;
     if (c < '0' || c > '9')
-      return addError("'" + std::string(token.start_, token.end_) +
-                          "' is not a number.",
-                      token);
-    Value::UInt digit(c - '0');
+      return decodeDouble(token, decoded);
+    auto digit(static_cast<Value::UInt>(c - '0'));
     if (value >= threshold) {
       // We've hit or exceeded the max value divided by 10 (rounded down). If
       // a) we've only just touched the limit, b) this is the last digit, and
@@ -544,7 +576,9 @@
     }
     value = value * 10 + digit;
   }
-  if (isNegative)
+  if (isNegative && value == maxIntegerValue)
+    decoded = Value::minLargestInt;
+  else if (isNegative)
     decoded = -Value::LargestInt(value);
   else if (value <= Value::LargestUInt(Value::maxInt))
     decoded = Value::LargestInt(value);
@@ -557,7 +591,7 @@
   Value decoded;
   if (!decodeDouble(token, decoded))
     return false;
-  currentValue() = decoded;
+  currentValue().swapPayload(decoded);
   currentValue().setOffsetStart(token.start_ - begin_);
   currentValue().setOffsetLimit(token.end_ - begin_);
   return true;
@@ -565,59 +599,35 @@
 
 bool Reader::decodeDouble(Token& token, Value& decoded) {
   double value = 0;
-  const int bufferSize = 32;
-  int count;
-  int length = int(token.end_ - token.start_);
-
-  // Sanity check to avoid buffer overflow exploits.
-  if (length < 0) {
-    return addError("Unable to parse token length", token);
-  }
-
-  // Avoid using a string constant for the format control string given to
-  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
-  // info:
-  //
-  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
-  char format[] = "%lf";
-
-  if (length <= bufferSize) {
-    Char buffer[bufferSize + 1];
-    memcpy(buffer, token.start_, length);
-    buffer[length] = 0;
-    count = sscanf(buffer, format, &value);
-  } else {
-    std::string buffer(token.start_, token.end_);
-    count = sscanf(buffer.c_str(), format, &value);
-  }
-
-  if (count != 1)
-    return addError("'" + std::string(token.start_, token.end_) +
-                        "' is not a number.",
-                    token);
+  String buffer(token.start_, token.end_);
+  IStringStream is(buffer);
+  if (!(is >> value))
+    return addError(
+        "'" + String(token.start_, token.end_) + "' is not a number.", token);
   decoded = value;
   return true;
 }
 
 bool Reader::decodeString(Token& token) {
-  std::string decoded;
-  if (!decodeString(token, decoded))
+  String decoded_string;
+  if (!decodeString(token, decoded_string))
     return false;
-  currentValue() = decoded;
+  Value decoded(decoded_string);
+  currentValue().swapPayload(decoded);
   currentValue().setOffsetStart(token.start_ - begin_);
   currentValue().setOffsetLimit(token.end_ - begin_);
   return true;
 }
 
-bool Reader::decodeString(Token& token, std::string& decoded) {
-  decoded.reserve(token.end_ - token.start_ - 2);
+bool Reader::decodeString(Token& token, String& decoded) {
+  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
   Location current = token.start_ + 1; // skip '"'
   Location end = token.end_ - 1;       // do not include '"'
   while (current != end) {
     Char c = *current++;
     if (c == '"')
       break;
-    else if (c == '\\') {
+    if (c == '\\') {
       if (current == end)
         return addError("Empty escape sequence in string", token, current);
       Char escape = *current++;
@@ -662,10 +672,8 @@
   return true;
 }
 
-bool Reader::decodeUnicodeCodePoint(Token& token,
-                                    Location& current,
-                                    Location end,
-                                    unsigned int& unicode) {
+bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
+                                    Location end, unsigned int& unicode) {
 
   if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
     return false;
@@ -674,10 +682,9 @@
     if (end - current < 6)
       return addError(
           "additional six characters expected to parse unicode surrogate pair.",
-          token,
-          current);
-    unsigned int surrogatePair;
+          token, current);
     if (*(current++) == '\\' && *(current++) == 'u') {
+      unsigned int surrogatePair;
       if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
         unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
       } else
@@ -685,22 +692,19 @@
     } else
       return addError("expecting another \\u token to begin the second half of "
                       "a unicode surrogate pair",
-                      token,
-                      current);
+                      token, current);
   }
   return true;
 }
 
-bool Reader::decodeUnicodeEscapeSequence(Token& token,
-                                         Location& current,
+bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
                                          Location end,
-                                         unsigned int& unicode) {
+                                         unsigned int& ret_unicode) {
   if (end - current < 4)
     return addError(
-        "Bad unicode escape sequence in string: four digits expected.",
-        token,
+        "Bad unicode escape sequence in string: four digits expected.", token,
         current);
-  unicode = 0;
+  int unicode = 0;
   for (int index = 0; index < 4; ++index) {
     Char c = *current++;
     unicode *= 16;
@@ -713,14 +717,13 @@
     else
       return addError(
           "Bad unicode escape sequence in string: hexadecimal digit expected.",
-          token,
-          current);
+          token, current);
   }
+  ret_unicode = static_cast<unsigned int>(unicode);
   return true;
 }
 
-bool
-Reader::addError(const std::string& message, Token& token, Location extra) {
+bool Reader::addError(const String& message, Token& token, Location extra) {
   ErrorInfo info;
   info.token_ = token;
   info.message_ = message;
@@ -730,7 +733,7 @@
 }
 
 bool Reader::recoverFromError(TokenType skipUntilToken) {
-  int errorCount = int(errors_.size());
+  size_t const errorCount = errors_.size();
   Token skip;
   for (;;) {
     if (!readToken(skip))
@@ -742,8 +745,7 @@
   return false;
 }
 
-bool Reader::addErrorAndRecover(const std::string& message,
-                                Token& token,
+bool Reader::addErrorAndRecover(const String& message, Token& token,
                                 TokenType skipUntilToken) {
   addError(message, token);
   return recoverFromError(skipUntilToken);
@@ -757,8 +759,7 @@
   return *current_++;
 }
 
-void Reader::getLocationLineAndColumn(Location location,
-                                      int& line,
+void Reader::getLocationLineAndColumn(Location location, int& line,
                                       int& column) const {
   Location current = begin_;
   Location lastLineStart = current;
@@ -780,33 +781,22 @@
   ++line;
 }
 
-std::string Reader::getLocationLineAndColumn(Location location) const {
+String Reader::getLocationLineAndColumn(Location location) const {
   int line, column;
   getLocationLineAndColumn(location, line, column);
   char buffer[18 + 16 + 16 + 1];
-#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
-#if defined(WINCE)
-  _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
-#else
-  sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
-#endif
-#else
-  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
-#endif
+  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
   return buffer;
 }
 
 // Deprecated. Preserved for backward compatibility
-std::string Reader::getFormatedErrorMessages() const {
+String Reader::getFormatedErrorMessages() const {
   return getFormattedErrorMessages();
 }
 
-std::string Reader::getFormattedErrorMessages() const {
-  std::string formattedMessage;
-  for (Errors::const_iterator itError = errors_.begin();
-       itError != errors_.end();
-       ++itError) {
-    const ErrorInfo& error = *itError;
+String Reader::getFormattedErrorMessages() const {
+  String formattedMessage;
+  for (const auto& error : errors_) {
     formattedMessage +=
         "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
     formattedMessage += "  " + error.message_ + "\n";
@@ -819,10 +809,7 @@
 
 std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
   std::vector<Reader::StructuredError> allErrors;
-  for (Errors::const_iterator itError = errors_.begin();
-       itError != errors_.end();
-       ++itError) {
-    const ErrorInfo& error = *itError;
+  for (const auto& error : errors_) {
     Reader::StructuredError structured;
     structured.offset_start = error.token_.start_ - begin_;
     structured.offset_limit = error.token_.end_ - begin_;
@@ -832,28 +819,27 @@
   return allErrors;
 }
 
-bool Reader::pushError(const Value& value, const std::string& message) {
-  size_t length = end_ - begin_;
-  if(value.getOffsetStart() > length
-    || value.getOffsetLimit() > length)
+bool Reader::pushError(const Value& value, const String& message) {
+  ptrdiff_t const length = end_ - begin_;
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
     return false;
   Token token;
   token.type_ = tokenError;
   token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = end_ + value.getOffsetLimit();
+  token.end_ = begin_ + value.getOffsetLimit();
   ErrorInfo info;
   info.token_ = token;
   info.message_ = message;
-  info.extra_ = 0;
+  info.extra_ = nullptr;
   errors_.push_back(info);
   return true;
 }
 
-bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) {
-  size_t length = end_ - begin_;
-  if(value.getOffsetStart() > length
-    || value.getOffsetLimit() > length
-    || extra.getOffsetLimit() > length)
+bool Reader::pushError(const Value& value, const String& message,
+                       const Value& extra) {
+  ptrdiff_t const length = end_ - begin_;
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
+      extra.getOffsetLimit() > length)
     return false;
   Token token;
   token.type_ = tokenError;
@@ -867,19 +853,1139 @@
   return true;
 }
 
-bool Reader::good() const {
-  return !errors_.size();
+bool Reader::good() const { return errors_.empty(); }
+
+// Originally copied from the Features class (now deprecated), used internally
+// for features implementation.
+class OurFeatures {
+public:
+  static OurFeatures all();
+  bool allowComments_;
+  bool allowTrailingCommas_;
+  bool strictRoot_;
+  bool allowDroppedNullPlaceholders_;
+  bool allowNumericKeys_;
+  bool allowSingleQuotes_;
+  bool failIfExtra_;
+  bool rejectDupKeys_;
+  bool allowSpecialFloats_;
+  bool skipBom_;
+  size_t stackLimit_;
+}; // OurFeatures
+
+OurFeatures OurFeatures::all() { return {}; }
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+// Originally copied from the Reader class (now deprecated), used internally
+// for implementing JSON reading.
+class OurReader {
+public:
+  using Char = char;
+  using Location = const Char*;
+  struct StructuredError {
+    ptrdiff_t offset_start;
+    ptrdiff_t offset_limit;
+    String message;
+  };
+
+  explicit OurReader(OurFeatures const& features);
+  bool parse(const char* beginDoc, const char* endDoc, Value& root,
+             bool collectComments = true);
+  String getFormattedErrorMessages() const;
+  std::vector<StructuredError> getStructuredErrors() const;
+
+private:
+  OurReader(OurReader const&);      // no impl
+  void operator=(OurReader const&); // no impl
+
+  enum TokenType {
+    tokenEndOfStream = 0,
+    tokenObjectBegin,
+    tokenObjectEnd,
+    tokenArrayBegin,
+    tokenArrayEnd,
+    tokenString,
+    tokenNumber,
+    tokenTrue,
+    tokenFalse,
+    tokenNull,
+    tokenNaN,
+    tokenPosInf,
+    tokenNegInf,
+    tokenArraySeparator,
+    tokenMemberSeparator,
+    tokenComment,
+    tokenError
+  };
+
+  class Token {
+  public:
+    TokenType type_;
+    Location start_;
+    Location end_;
+  };
+
+  class ErrorInfo {
+  public:
+    Token token_;
+    String message_;
+    Location extra_;
+  };
+
+  using Errors = std::deque<ErrorInfo>;
+
+  bool readToken(Token& token);
+  void skipSpaces();
+  void skipBom(bool skipBom);
+  bool match(const Char* pattern, int patternLength);
+  bool readComment();
+  bool readCStyleComment(bool* containsNewLineResult);
+  bool readCppStyleComment();
+  bool readString();
+  bool readStringSingleQuote();
+  bool readNumber(bool checkInf);
+  bool readValue();
+  bool readObject(Token& token);
+  bool readArray(Token& token);
+  bool decodeNumber(Token& token);
+  bool decodeNumber(Token& token, Value& decoded);
+  bool decodeString(Token& token);
+  bool decodeString(Token& token, String& decoded);
+  bool decodeDouble(Token& token);
+  bool decodeDouble(Token& token, Value& decoded);
+  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
+                              unsigned int& unicode);
+  bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+                                   Location end, unsigned int& unicode);
+  bool addError(const String& message, Token& token, Location extra = nullptr);
+  bool recoverFromError(TokenType skipUntilToken);
+  bool addErrorAndRecover(const String& message, Token& token,
+                          TokenType skipUntilToken);
+  void skipUntilSpace();
+  Value& currentValue();
+  Char getNextChar();
+  void getLocationLineAndColumn(Location location, int& line,
+                                int& column) const;
+  String getLocationLineAndColumn(Location location) const;
+  void addComment(Location begin, Location end, CommentPlacement placement);
+  void skipCommentTokens(Token& token);
+
+  static String normalizeEOL(Location begin, Location end);
+  static bool containsNewLine(Location begin, Location end);
+
+  using Nodes = std::stack<Value*>;
+
+  Nodes nodes_{};
+  Errors errors_{};
+  String document_{};
+  Location begin_ = nullptr;
+  Location end_ = nullptr;
+  Location current_ = nullptr;
+  Location lastValueEnd_ = nullptr;
+  Value* lastValue_ = nullptr;
+  bool lastValueHasAComment_ = false;
+  String commentsBefore_{};
+
+  OurFeatures const features_;
+  bool collectComments_ = false;
+}; // OurReader
+
+// complete copy of Read impl, for OurReader
+
+bool OurReader::containsNewLine(OurReader::Location begin,
+                                OurReader::Location end) {
+  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
 }
 
-std::istream& operator>>(std::istream& sin, Value& root) {
-  Json::Reader reader;
-  bool ok = reader.parse(sin, root, true);
-  if (!ok) {
-    fprintf(stderr,
-            "Error from reader: %s",
-            reader.getFormattedErrorMessages().c_str());
+OurReader::OurReader(OurFeatures const& features) : features_(features) {}
 
-    JSON_FAIL_MESSAGE("reader error");
+bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
+                      bool collectComments) {
+  if (!features_.allowComments_) {
+    collectComments = false;
+  }
+
+  begin_ = beginDoc;
+  end_ = endDoc;
+  collectComments_ = collectComments;
+  current_ = begin_;
+  lastValueEnd_ = nullptr;
+  lastValue_ = nullptr;
+  commentsBefore_.clear();
+  errors_.clear();
+  while (!nodes_.empty())
+    nodes_.pop();
+  nodes_.push(&root);
+
+  // skip byte order mark if it exists at the beginning of the UTF-8 text.
+  skipBom(features_.skipBom_);
+  bool successful = readValue();
+  nodes_.pop();
+  Token token;
+  skipCommentTokens(token);
+  if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
+    addError("Extra non-whitespace after JSON value.", token);
+    return false;
+  }
+  if (collectComments_ && !commentsBefore_.empty())
+    root.setComment(commentsBefore_, commentAfter);
+  if (features_.strictRoot_) {
+    if (!root.isArray() && !root.isObject()) {
+      // Set error location to start of doc, ideally should be first token found
+      // in doc
+      token.type_ = tokenError;
+      token.start_ = beginDoc;
+      token.end_ = endDoc;
+      addError(
+          "A valid JSON document must be either an array or an object value.",
+          token);
+      return false;
+    }
+  }
+  return successful;
+}
+
+bool OurReader::readValue() {
+  //  To preserve the old behaviour we cast size_t to int.
+  if (nodes_.size() > features_.stackLimit_)
+    throwRuntimeError("Exceeded stackLimit in readValue().");
+  Token token;
+  skipCommentTokens(token);
+  bool successful = true;
+
+  if (collectComments_ && !commentsBefore_.empty()) {
+    currentValue().setComment(commentsBefore_, commentBefore);
+    commentsBefore_.clear();
+  }
+
+  switch (token.type_) {
+  case tokenObjectBegin:
+    successful = readObject(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenArrayBegin:
+    successful = readArray(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenNumber:
+    successful = decodeNumber(token);
+    break;
+  case tokenString:
+    successful = decodeString(token);
+    break;
+  case tokenTrue: {
+    Value v(true);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenFalse: {
+    Value v(false);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNull: {
+    Value v;
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNaN: {
+    Value v(std::numeric_limits<double>::quiet_NaN());
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenPosInf: {
+    Value v(std::numeric_limits<double>::infinity());
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNegInf: {
+    Value v(-std::numeric_limits<double>::infinity());
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenArraySeparator:
+  case tokenObjectEnd:
+  case tokenArrayEnd:
+    if (features_.allowDroppedNullPlaceholders_) {
+      // "Un-read" the current token and mark the current value as a null
+      // token.
+      current_--;
+      Value v;
+      currentValue().swapPayload(v);
+      currentValue().setOffsetStart(current_ - begin_ - 1);
+      currentValue().setOffsetLimit(current_ - begin_);
+      break;
+    } // else, fall through ...
+  default:
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    return addError("Syntax error: value, object or array expected.", token);
+  }
+
+  if (collectComments_) {
+    lastValueEnd_ = current_;
+    lastValueHasAComment_ = false;
+    lastValue_ = &currentValue();
+  }
+
+  return successful;
+}
+
+void OurReader::skipCommentTokens(Token& token) {
+  if (features_.allowComments_) {
+    do {
+      readToken(token);
+    } while (token.type_ == tokenComment);
+  } else {
+    readToken(token);
+  }
+}
+
+bool OurReader::readToken(Token& token) {
+  skipSpaces();
+  token.start_ = current_;
+  Char c = getNextChar();
+  bool ok = true;
+  switch (c) {
+  case '{':
+    token.type_ = tokenObjectBegin;
+    break;
+  case '}':
+    token.type_ = tokenObjectEnd;
+    break;
+  case '[':
+    token.type_ = tokenArrayBegin;
+    break;
+  case ']':
+    token.type_ = tokenArrayEnd;
+    break;
+  case '"':
+    token.type_ = tokenString;
+    ok = readString();
+    break;
+  case '\'':
+    if (features_.allowSingleQuotes_) {
+      token.type_ = tokenString;
+      ok = readStringSingleQuote();
+    } else {
+      // If we don't allow single quotes, this is a failure case.
+      ok = false;
+    }
+    break;
+  case '/':
+    token.type_ = tokenComment;
+    ok = readComment();
+    break;
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+    token.type_ = tokenNumber;
+    readNumber(false);
+    break;
+  case '-':
+    if (readNumber(true)) {
+      token.type_ = tokenNumber;
+    } else {
+      token.type_ = tokenNegInf;
+      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+    }
+    break;
+  case '+':
+    if (readNumber(true)) {
+      token.type_ = tokenNumber;
+    } else {
+      token.type_ = tokenPosInf;
+      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+    }
+    break;
+  case 't':
+    token.type_ = tokenTrue;
+    ok = match("rue", 3);
+    break;
+  case 'f':
+    token.type_ = tokenFalse;
+    ok = match("alse", 4);
+    break;
+  case 'n':
+    token.type_ = tokenNull;
+    ok = match("ull", 3);
+    break;
+  case 'N':
+    if (features_.allowSpecialFloats_) {
+      token.type_ = tokenNaN;
+      ok = match("aN", 2);
+    } else {
+      ok = false;
+    }
+    break;
+  case 'I':
+    if (features_.allowSpecialFloats_) {
+      token.type_ = tokenPosInf;
+      ok = match("nfinity", 7);
+    } else {
+      ok = false;
+    }
+    break;
+  case ',':
+    token.type_ = tokenArraySeparator;
+    break;
+  case ':':
+    token.type_ = tokenMemberSeparator;
+    break;
+  case 0:
+    token.type_ = tokenEndOfStream;
+    break;
+  default:
+    ok = false;
+    break;
+  }
+  if (!ok)
+    token.type_ = tokenError;
+  token.end_ = current_;
+  return ok;
+}
+
+void OurReader::skipSpaces() {
+  while (current_ != end_) {
+    Char c = *current_;
+    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+      ++current_;
+    else
+      break;
+  }
+}
+
+void OurReader::skipBom(bool skipBom) {
+  // The default behavior is to skip BOM.
+  if (skipBom) {
+    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
+      begin_ += 3;
+      current_ = begin_;
+    }
+  }
+}
+
+bool OurReader::match(const Char* pattern, int patternLength) {
+  if (end_ - current_ < patternLength)
+    return false;
+  int index = patternLength;
+  while (index--)
+    if (current_[index] != pattern[index])
+      return false;
+  current_ += patternLength;
+  return true;
+}
+
+bool OurReader::readComment() {
+  const Location commentBegin = current_ - 1;
+  const Char c = getNextChar();
+  bool successful = false;
+  bool cStyleWithEmbeddedNewline = false;
+
+  const bool isCStyleComment = (c == '*');
+  const bool isCppStyleComment = (c == '/');
+  if (isCStyleComment) {
+    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
+  } else if (isCppStyleComment) {
+    successful = readCppStyleComment();
+  }
+
+  if (!successful)
+    return false;
+
+  if (collectComments_) {
+    CommentPlacement placement = commentBefore;
+
+    if (!lastValueHasAComment_) {
+      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
+          placement = commentAfterOnSameLine;
+          lastValueHasAComment_ = true;
+        }
+      }
+    }
+
+    addComment(commentBegin, current_, placement);
+  }
+  return true;
+}
+
+String OurReader::normalizeEOL(OurReader::Location begin,
+                               OurReader::Location end) {
+  String normalized;
+  normalized.reserve(static_cast<size_t>(end - begin));
+  OurReader::Location current = begin;
+  while (current != end) {
+    char c = *current++;
+    if (c == '\r') {
+      if (current != end && *current == '\n')
+        // convert dos EOL
+        ++current;
+      // convert Mac EOL
+      normalized += '\n';
+    } else {
+      normalized += c;
+    }
+  }
+  return normalized;
+}
+
+void OurReader::addComment(Location begin, Location end,
+                           CommentPlacement placement) {
+  assert(collectComments_);
+  const String& normalized = normalizeEOL(begin, end);
+  if (placement == commentAfterOnSameLine) {
+    assert(lastValue_ != nullptr);
+    lastValue_->setComment(normalized, placement);
+  } else {
+    commentsBefore_ += normalized;
+  }
+}
+
+bool OurReader::readCStyleComment(bool* containsNewLineResult) {
+  *containsNewLineResult = false;
+
+  while ((current_ + 1) < end_) {
+    Char c = getNextChar();
+    if (c == '*' && *current_ == '/')
+      break;
+    if (c == '\n')
+      *containsNewLineResult = true;
+  }
+
+  return getNextChar() == '/';
+}
+
+bool OurReader::readCppStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '\n')
+      break;
+    if (c == '\r') {
+      // Consume DOS EOL. It will be normalized in addComment.
+      if (current_ != end_ && *current_ == '\n')
+        getNextChar();
+      // Break on Moc OS 9 EOL.
+      break;
+    }
+  }
+  return true;
+}
+
+bool OurReader::readNumber(bool checkInf) {
+  Location p = current_;
+  if (checkInf && p != end_ && *p == 'I') {
+    current_ = ++p;
+    return false;
+  }
+  char c = '0'; // stopgap for already consumed character
+  // integral part
+  while (c >= '0' && c <= '9')
+    c = (current_ = p) < end_ ? *p++ : '\0';
+  // fractional part
+  if (c == '.') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+  // exponential part
+  if (c == 'e' || c == 'E') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    if (c == '+' || c == '-')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+  return true;
+}
+bool OurReader::readString() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '"')
+      break;
+  }
+  return c == '"';
+}
+
+bool OurReader::readStringSingleQuote() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '\'')
+      break;
+  }
+  return c == '\'';
+}
+
+bool OurReader::readObject(Token& token) {
+  Token tokenName;
+  String name;
+  Value init(objectValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  while (readToken(tokenName)) {
+    bool initialTokenOk = true;
+    while (tokenName.type_ == tokenComment && initialTokenOk)
+      initialTokenOk = readToken(tokenName);
+    if (!initialTokenOk)
+      break;
+    if (tokenName.type_ == tokenObjectEnd &&
+        (name.empty() ||
+         features_.allowTrailingCommas_)) // empty object or trailing comma
+      return true;
+    name.clear();
+    if (tokenName.type_ == tokenString) {
+      if (!decodeString(tokenName, name))
+        return recoverFromError(tokenObjectEnd);
+    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+      Value numberName;
+      if (!decodeNumber(tokenName, numberName))
+        return recoverFromError(tokenObjectEnd);
+      name = numberName.asString();
+    } else {
+      break;
+    }
+    if (name.length() >= (1U << 30))
+      throwRuntimeError("keylength >= 2^30");
+    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
+      String msg = "Duplicate key: '" + name + "'";
+      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
+    }
+
+    Token colon;
+    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+      return addErrorAndRecover("Missing ':' after object member name", colon,
+                                tokenObjectEnd);
+    }
+    Value& value = currentValue()[name];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenObjectEnd);
+
+    Token comma;
+    if (!readToken(comma) ||
+        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+         comma.type_ != tokenComment)) {
+      return addErrorAndRecover("Missing ',' or '}' in object declaration",
+                                comma, tokenObjectEnd);
+    }
+    bool finalizeTokenOk = true;
+    while (comma.type_ == tokenComment && finalizeTokenOk)
+      finalizeTokenOk = readToken(comma);
+    if (comma.type_ == tokenObjectEnd)
+      return true;
+  }
+  return addErrorAndRecover("Missing '}' or object member name", tokenName,
+                            tokenObjectEnd);
+}
+
+bool OurReader::readArray(Token& token) {
+  Value init(arrayValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  int index = 0;
+  for (;;) {
+    skipSpaces();
+    if (current_ != end_ && *current_ == ']' &&
+        (index == 0 ||
+         (features_.allowTrailingCommas_ &&
+          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
+                                                      // comma
+    {
+      Token endArray;
+      readToken(endArray);
+      return true;
+    }
+    Value& value = currentValue()[index++];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenArrayEnd);
+
+    Token currentToken;
+    // Accept Comment after last item in the array.
+    ok = readToken(currentToken);
+    while (currentToken.type_ == tokenComment && ok) {
+      ok = readToken(currentToken);
+    }
+    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+                         currentToken.type_ != tokenArrayEnd);
+    if (!ok || badTokenType) {
+      return addErrorAndRecover("Missing ',' or ']' in array declaration",
+                                currentToken, tokenArrayEnd);
+    }
+    if (currentToken.type_ == tokenArrayEnd)
+      break;
+  }
+  return true;
+}
+
+bool OurReader::decodeNumber(Token& token) {
+  Value decoded;
+  if (!decodeNumber(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeNumber(Token& token, Value& decoded) {
+  // Attempts to parse the number as an integer. If the number is
+  // larger than the maximum supported value of an integer then
+  // we decode the number as a double.
+  Location current = token.start_;
+  const bool isNegative = *current == '-';
+  if (isNegative) {
+    ++current;
+  }
+
+  // We assume we can represent the largest and smallest integer types as
+  // unsigned integers with separate sign. This is only true if they can fit
+  // into an unsigned integer.
+  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
+                "Int must be smaller than UInt");
+
+  // We need to convert minLargestInt into a positive number. The easiest way
+  // to do this conversion is to assume our "threshold" value of minLargestInt
+  // divided by 10 can fit in maxLargestInt when absolute valued. This should
+  // be a safe assumption.
+  static_assert(Value::minLargestInt <= -Value::maxLargestInt,
+                "The absolute value of minLargestInt must be greater than or "
+                "equal to maxLargestInt");
+  static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
+                "The absolute value of minLargestInt must be only 1 magnitude "
+                "larger than maxLargest Int");
+
+  static constexpr Value::LargestUInt positive_threshold =
+      Value::maxLargestUInt / 10;
+  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
+
+  // For the negative values, we have to be more careful. Since typically
+  // -Value::minLargestInt will cause an overflow, we first divide by 10 and
+  // then take the inverse. This assumes that minLargestInt is only a single
+  // power of 10 different in magnitude, which we check above. For the last
+  // digit, we take the modulus before negating for the same reason.
+  static constexpr auto negative_threshold =
+      Value::LargestUInt(-(Value::minLargestInt / 10));
+  static constexpr auto negative_last_digit =
+      Value::UInt(-(Value::minLargestInt % 10));
+
+  const Value::LargestUInt threshold =
+      isNegative ? negative_threshold : positive_threshold;
+  const Value::UInt max_last_digit =
+      isNegative ? negative_last_digit : positive_last_digit;
+
+  Value::LargestUInt value = 0;
+  while (current < token.end_) {
+    Char c = *current++;
+    if (c < '0' || c > '9')
+      return decodeDouble(token, decoded);
+
+    const auto digit(static_cast<Value::UInt>(c - '0'));
+    if (value >= threshold) {
+      // We've hit or exceeded the max value divided by 10 (rounded down). If
+      // a) we've only just touched the limit, meaing value == threshold,
+      // b) this is the last digit, or
+      // c) it's small enough to fit in that rounding delta, we're okay.
+      // Otherwise treat this number as a double to avoid overflow.
+      if (value > threshold || current != token.end_ ||
+          digit > max_last_digit) {
+        return decodeDouble(token, decoded);
+      }
+    }
+    value = value * 10 + digit;
+  }
+
+  if (isNegative) {
+    // We use the same magnitude assumption here, just in case.
+    const auto last_digit = static_cast<Value::UInt>(value % 10);
+    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
+  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
+    decoded = Value::LargestInt(value);
+  } else {
+    decoded = value;
+  }
+
+  return true;
+}
+
+bool OurReader::decodeDouble(Token& token) {
+  Value decoded;
+  if (!decodeDouble(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeDouble(Token& token, Value& decoded) {
+  double value = 0;
+  const String buffer(token.start_, token.end_);
+  IStringStream is(buffer);
+  if (!(is >> value)) {
+    return addError(
+        "'" + String(token.start_, token.end_) + "' is not a number.", token);
+  }
+  decoded = value;
+  return true;
+}
+
+bool OurReader::decodeString(Token& token) {
+  String decoded_string;
+  if (!decodeString(token, decoded_string))
+    return false;
+  Value decoded(decoded_string);
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeString(Token& token, String& decoded) {
+  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
+  Location current = token.start_ + 1; // skip '"'
+  Location end = token.end_ - 1;       // do not include '"'
+  while (current != end) {
+    Char c = *current++;
+    if (c == '"')
+      break;
+    if (c == '\\') {
+      if (current == end)
+        return addError("Empty escape sequence in string", token, current);
+      Char escape = *current++;
+      switch (escape) {
+      case '"':
+        decoded += '"';
+        break;
+      case '/':
+        decoded += '/';
+        break;
+      case '\\':
+        decoded += '\\';
+        break;
+      case 'b':
+        decoded += '\b';
+        break;
+      case 'f':
+        decoded += '\f';
+        break;
+      case 'n':
+        decoded += '\n';
+        break;
+      case 'r':
+        decoded += '\r';
+        break;
+      case 't':
+        decoded += '\t';
+        break;
+      case 'u': {
+        unsigned int unicode;
+        if (!decodeUnicodeCodePoint(token, current, end, unicode))
+          return false;
+        decoded += codePointToUTF8(unicode);
+      } break;
+      default:
+        return addError("Bad escape sequence in string", token, current);
+      }
+    } else {
+      decoded += c;
+    }
+  }
+  return true;
+}
+
+bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
+                                       Location end, unsigned int& unicode) {
+
+  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+    return false;
+  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+    // surrogate pairs
+    if (end - current < 6)
+      return addError(
+          "additional six characters expected to parse unicode surrogate pair.",
+          token, current);
+    if (*(current++) == '\\' && *(current++) == 'u') {
+      unsigned int surrogatePair;
+      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+      } else
+        return false;
+    } else
+      return addError("expecting another \\u token to begin the second half of "
+                      "a unicode surrogate pair",
+                      token, current);
+  }
+  return true;
+}
+
+bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
+                                            Location end,
+                                            unsigned int& ret_unicode) {
+  if (end - current < 4)
+    return addError(
+        "Bad unicode escape sequence in string: four digits expected.", token,
+        current);
+  int unicode = 0;
+  for (int index = 0; index < 4; ++index) {
+    Char c = *current++;
+    unicode *= 16;
+    if (c >= '0' && c <= '9')
+      unicode += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      unicode += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      unicode += c - 'A' + 10;
+    else
+      return addError(
+          "Bad unicode escape sequence in string: hexadecimal digit expected.",
+          token, current);
+  }
+  ret_unicode = static_cast<unsigned int>(unicode);
+  return true;
+}
+
+bool OurReader::addError(const String& message, Token& token, Location extra) {
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = extra;
+  errors_.push_back(info);
+  return false;
+}
+
+bool OurReader::recoverFromError(TokenType skipUntilToken) {
+  size_t errorCount = errors_.size();
+  Token skip;
+  for (;;) {
+    if (!readToken(skip))
+      errors_.resize(errorCount); // discard errors caused by recovery
+    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+      break;
+  }
+  errors_.resize(errorCount);
+  return false;
+}
+
+bool OurReader::addErrorAndRecover(const String& message, Token& token,
+                                   TokenType skipUntilToken) {
+  addError(message, token);
+  return recoverFromError(skipUntilToken);
+}
+
+Value& OurReader::currentValue() { return *(nodes_.top()); }
+
+OurReader::Char OurReader::getNextChar() {
+  if (current_ == end_)
+    return 0;
+  return *current_++;
+}
+
+void OurReader::getLocationLineAndColumn(Location location, int& line,
+                                         int& column) const {
+  Location current = begin_;
+  Location lastLineStart = current;
+  line = 0;
+  while (current < location && current != end_) {
+    Char c = *current++;
+    if (c == '\r') {
+      if (*current == '\n')
+        ++current;
+      lastLineStart = current;
+      ++line;
+    } else if (c == '\n') {
+      lastLineStart = current;
+      ++line;
+    }
+  }
+  // column & line start at 1
+  column = int(location - lastLineStart) + 1;
+  ++line;
+}
+
+String OurReader::getLocationLineAndColumn(Location location) const {
+  int line, column;
+  getLocationLineAndColumn(location, line, column);
+  char buffer[18 + 16 + 16 + 1];
+  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+  return buffer;
+}
+
+String OurReader::getFormattedErrorMessages() const {
+  String formattedMessage;
+  for (const auto& error : errors_) {
+    formattedMessage +=
+        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+    formattedMessage += "  " + error.message_ + "\n";
+    if (error.extra_)
+      formattedMessage +=
+          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+  }
+  return formattedMessage;
+}
+
+std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
+  std::vector<OurReader::StructuredError> allErrors;
+  for (const auto& error : errors_) {
+    OurReader::StructuredError structured;
+    structured.offset_start = error.token_.start_ - begin_;
+    structured.offset_limit = error.token_.end_ - begin_;
+    structured.message = error.message_;
+    allErrors.push_back(structured);
+  }
+  return allErrors;
+}
+
+class OurCharReader : public CharReader {
+  bool const collectComments_;
+  OurReader reader_;
+
+public:
+  OurCharReader(bool collectComments, OurFeatures const& features)
+      : collectComments_(collectComments), reader_(features) {}
+  bool parse(char const* beginDoc, char const* endDoc, Value* root,
+             String* errs) override {
+    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
+    if (errs) {
+      *errs = reader_.getFormattedErrorMessages();
+    }
+    return ok;
+  }
+};
+
+CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
+CharReaderBuilder::~CharReaderBuilder() = default;
+CharReader* CharReaderBuilder::newCharReader() const {
+  bool collectComments = settings_["collectComments"].asBool();
+  OurFeatures features = OurFeatures::all();
+  features.allowComments_ = settings_["allowComments"].asBool();
+  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
+  features.strictRoot_ = settings_["strictRoot"].asBool();
+  features.allowDroppedNullPlaceholders_ =
+      settings_["allowDroppedNullPlaceholders"].asBool();
+  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
+  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
+
+  // Stack limit is always a size_t, so we get this as an unsigned int
+  // regardless of it we have 64-bit integer support enabled.
+  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
+  features.failIfExtra_ = settings_["failIfExtra"].asBool();
+  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
+  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
+  features.skipBom_ = settings_["skipBom"].asBool();
+  return new OurCharReader(collectComments, features);
+}
+
+bool CharReaderBuilder::validate(Json::Value* invalid) const {
+  static const auto& valid_keys = *new std::set<String>{
+      "collectComments",
+      "allowComments",
+      "allowTrailingCommas",
+      "strictRoot",
+      "allowDroppedNullPlaceholders",
+      "allowNumericKeys",
+      "allowSingleQuotes",
+      "stackLimit",
+      "failIfExtra",
+      "rejectDupKeys",
+      "allowSpecialFloats",
+      "skipBom",
+  };
+  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+    auto key = si.name();
+    if (valid_keys.count(key))
+      continue;
+    if (invalid)
+      (*invalid)[std::move(key)] = *si;
+    else
+      return false;
+  }
+  return invalid ? invalid->empty() : true;
+}
+
+Value& CharReaderBuilder::operator[](const String& key) {
+  return settings_[key];
+}
+// static
+void CharReaderBuilder::strictMode(Json::Value* settings) {
+  //! [CharReaderBuilderStrictMode]
+  (*settings)["allowComments"] = false;
+  (*settings)["allowTrailingCommas"] = false;
+  (*settings)["strictRoot"] = true;
+  (*settings)["allowDroppedNullPlaceholders"] = false;
+  (*settings)["allowNumericKeys"] = false;
+  (*settings)["allowSingleQuotes"] = false;
+  (*settings)["stackLimit"] = 1000;
+  (*settings)["failIfExtra"] = true;
+  (*settings)["rejectDupKeys"] = true;
+  (*settings)["allowSpecialFloats"] = false;
+  (*settings)["skipBom"] = true;
+  //! [CharReaderBuilderStrictMode]
+}
+// static
+void CharReaderBuilder::setDefaults(Json::Value* settings) {
+  //! [CharReaderBuilderDefaults]
+  (*settings)["collectComments"] = true;
+  (*settings)["allowComments"] = true;
+  (*settings)["allowTrailingCommas"] = true;
+  (*settings)["strictRoot"] = false;
+  (*settings)["allowDroppedNullPlaceholders"] = false;
+  (*settings)["allowNumericKeys"] = false;
+  (*settings)["allowSingleQuotes"] = false;
+  (*settings)["stackLimit"] = 1000;
+  (*settings)["failIfExtra"] = false;
+  (*settings)["rejectDupKeys"] = false;
+  (*settings)["allowSpecialFloats"] = false;
+  (*settings)["skipBom"] = true;
+  //! [CharReaderBuilderDefaults]
+}
+
+//////////////////////////////////
+// global functions
+
+bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
+                     String* errs) {
+  OStringStream ssin;
+  ssin << sin.rdbuf();
+  String doc = ssin.str();
+  char const* begin = doc.data();
+  char const* end = begin + doc.size();
+  // Note that we do not actually need a null-terminator.
+  CharReaderPtr const reader(fact.newCharReader());
+  return reader->parse(begin, end, root, errs);
+}
+
+IStream& operator>>(IStream& sin, Value& root) {
+  CharReaderBuilder b;
+  String errs;
+  bool ok = parseFromStream(b, sin, &root, &errs);
+  if (!ok) {
+    throwRuntimeError(errs);
   }
   return sin;
 }