// 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/assertions.h>
#  include <json/reader.h>
#  include <json/value.h>

#  include "json_tool.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <istream>
#include <limits>
#include <memory>
#include <set>
#include <sstream>
#include <utility>

#include <assert.h>
#include <stdio.h>
#include <string.h>

#if defined(_MSC_VER)
#  if !defined(WINCE) && defined(__STDC_SECURE_LIB__) &&                      \
    _MSC_VER >= 1500 // VC++ 9.0 and above
#    define snprintf sprintf_s
#  elif _MSC_VER >= 1900 // VC++ 14.0 and above
#    define snprintf std::snprintf
#  else
#    define snprintf _snprintf
#  endif
#elif defined(__ANDROID__) || defined(__QNXNTO__)
#  define snprintf snprintf
#elif __cplusplus >= 201103L
#  if !defined(__MINGW32__) && !defined(__CYGWIN__)
#    define snprintf std::snprintf
#  endif
#endif

#if defined(__QNXNTO__)
#  define sscanf std::sscanf
#endif

#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
// 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)
typedef std::unique_ptr<CharReader> CharReaderPtr;
#else
typedef std::auto_ptr<CharReader> CharReaderPtr;
#endif

// Implementation of class Features
// ////////////////////////////////

Features::Features()
  : allowComments_(true)
  , strictRoot_(false)
  , allowDroppedNullPlaceholders_(false)
  , allowNumericKeys_(false)
{
}

Features Features::all()
{
  return Features();
}

Features Features::strictMode()
{
  Features features;
  features.allowComments_ = false;
  features.strictRoot_ = true;
  features.allowDroppedNullPlaceholders_ = false;
  features.allowNumericKeys_ = false;
  return features;
}

// Implementation of class Reader
// ////////////////////////////////

bool Reader::containsNewLine(Reader::Location begin, Reader::Location end)
{
  for (; begin < end; ++begin)
    if (*begin == '\n' || *begin == '\r')
      return true;
  return false;
}

// Class Reader
// //////////////////////////////////////////////////////////////////

Reader::Reader()
  : errors_()
  , document_()
  , begin_()
  , end_()
  , current_()
  , lastValueEnd_()
  , lastValue_()
  , commentsBefore_()
  , features_(Features::all())
  , collectComments_()
{
}

Reader::Reader(const Features& features)
  : errors_()
  , document_()
  , begin_()
  , end_()
  , current_()
  , lastValueEnd_()
  , lastValue_()
  , commentsBefore_()
  , features_(features)
  , collectComments_()
{
}

bool Reader::parse(const std::string& document, Value& root,
                   bool collectComments)
{
  this->document_.assign(document.begin(), document.end());
  const char* begin = this->document_.c_str();
  const char* end = begin + this->document_.length();
  return this->parse(begin, end, root, collectComments);
}

bool Reader::parse(std::istream& sin, Value& root, bool collectComments)
{
  // std::istream_iterator<char> begin(sin);
  // std::istream_iterator<char> end;
  // Those would allow streamed input from a file, if parse() were a
  // template function.

  // Since JSONCPP_STRING is reference-counted, this at least does not
  // create an extra copy.
  JSONCPP_STRING doc;
  std::getline(sin, doc, (char)EOF);
  return this->parse(doc.data(), doc.data() + doc.size(), root,
                     collectComments);
}

bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
                   bool collectComments)
{
  if (!this->features_.allowComments_) {
    collectComments = false;
  }

  this->begin_ = beginDoc;
  this->end_ = endDoc;
  this->collectComments_ = collectComments;
  this->current_ = this->begin_;
  this->lastValueEnd_ = 0;
  this->lastValue_ = 0;
  this->commentsBefore_.clear();
  this->errors_.clear();
  while (!this->nodes_.empty())
    this->nodes_.pop();
  this->nodes_.push(&root);

  bool successful = this->readValue();
  Token token;
  this->skipCommentTokens(token);
  if (this->collectComments_ && !this->commentsBefore_.empty())
    root.setComment(this->commentsBefore_, commentAfter);
  if (this->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;
      this->addError(
        "A valid JSON document must be either an array or an object value.",
        token);
      return false;
    }
  }
  return successful;
}

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 (this->nodes_.size() > stackLimit_g)
    throwRuntimeError("Exceeded stackLimit in readValue().");

  Token token;
  this->skipCommentTokens(token);
  bool successful = true;

  if (this->collectComments_ && !this->commentsBefore_.empty()) {
    this->currentValue().setComment(this->commentsBefore_, commentBefore);
    this->commentsBefore_.clear();
  }

  switch (token.type_) {
    case tokenObjectBegin:
      successful = this->readObject(token);
      this->currentValue().setOffsetLimit(this->current_ - this->begin_);
      break;
    case tokenArrayBegin:
      successful = this->readArray(token);
      this->currentValue().setOffsetLimit(this->current_ - this->begin_);
      break;
    case tokenNumber:
      successful = this->decodeNumber(token);
      break;
    case tokenString:
      successful = this->decodeString(token);
      break;
    case tokenTrue: {
      Value v(true);
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenFalse: {
      Value v(false);
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenNull: {
      Value v;
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenArraySeparator:
    case tokenObjectEnd:
    case tokenArrayEnd:
      if (this->features_.allowDroppedNullPlaceholders_) {
        // "Un-read" the current token and mark the current value as a null
        // token.
        this->current_--;
        Value v;
        this->currentValue().swapPayload(v);
        this->currentValue().setOffsetStart(this->current_ - this->begin_ - 1);
        this->currentValue().setOffsetLimit(this->current_ - this->begin_);
        break;
      } // Else, fall through...
    default:
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
      return this->addError("Syntax error: value, object or array expected.",
                            token);
  }

  if (this->collectComments_) {
    this->lastValueEnd_ = this->current_;
    this->lastValue_ = &this->currentValue();
  }

  return successful;
}

void Reader::skipCommentTokens(Token& token)
{
  if (this->features_.allowComments_) {
    do {
      this->readToken(token);
    } while (token.type_ == tokenComment);
  } else {
    this->readToken(token);
  }
}

bool Reader::readToken(Token& token)
{
  this->skipSpaces();
  token.start_ = this->current_;
  Char c = this->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 = this->readString();
      break;
    case '/':
      token.type_ = tokenComment;
      ok = this->readComment();
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '-':
      token.type_ = tokenNumber;
      this->readNumber();
      break;
    case 't':
      token.type_ = tokenTrue;
      ok = this->match("rue", 3);
      break;
    case 'f':
      token.type_ = tokenFalse;
      ok = this->match("alse", 4);
      break;
    case 'n':
      token.type_ = tokenNull;
      ok = this->match("ull", 3);
      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_ = this->current_;
  return true;
}

void Reader::skipSpaces()
{
  while (this->current_ != this->end_) {
    Char c = *this->current_;
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
      ++this->current_;
    else
      break;
  }
}

bool Reader::match(Location pattern, int patternLength)
{
  if (this->end_ - this->current_ < patternLength)
    return false;
  int index = patternLength;
  while (index--)
    if (this->current_[index] != pattern[index])
      return false;
  this->current_ += patternLength;
  return true;
}

bool Reader::readComment()
{
  Location commentBegin = this->current_ - 1;
  Char c = this->getNextChar();
  bool successful = false;
  if (c == '*')
    successful = this->readCStyleComment();
  else if (c == '/')
    successful = this->readCppStyleComment();
  if (!successful)
    return false;

  if (this->collectComments_) {
    CommentPlacement placement = commentBefore;
    if (this->lastValueEnd_ &&
        !containsNewLine(this->lastValueEnd_, commentBegin)) {
      if (c != '*' || !containsNewLine(commentBegin, this->current_))
        placement = commentAfterOnSameLine;
    }

    this->addComment(commentBegin, this->current_, placement);
  }
  return true;
}

JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin,
                                    Reader::Location end)
{
  JSONCPP_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(this->collectComments_);
  const JSONCPP_STRING& normalized = normalizeEOL(begin, end);
  if (placement == commentAfterOnSameLine) {
    assert(this->lastValue_ != 0);
    this->lastValue_->setComment(normalized, placement);
  } else {
    this->commentsBefore_ += normalized;
  }
}

bool Reader::readCStyleComment()
{
  while ((this->current_ + 1) < this->end_) {
    Char c = this->getNextChar();
    if (c == '*' && *this->current_ == '/')
      break;
  }
  return this->getNextChar() == '/';
}

bool Reader::readCppStyleComment()
{
  while (this->current_ != this->end_) {
    Char c = this->getNextChar();
    if (c == '\n')
      break;
    if (c == '\r') {
      // Consume DOS EOL. It will be normalized in addComment.
      if (this->current_ != this->end_ && *this->current_ == '\n')
        this->getNextChar();
      // Break on Moc OS 9 EOL.
      break;
    }
  }
  return true;
}

void Reader::readNumber()
{
  const char* p = this->current_;
  char c = '0'; // stopgap for already consumed character
  // integral part
  while (c >= '0' && c <= '9')
    c = (this->current_ = p) < this->end_ ? *p++ : '\0';
  // fractional part
  if (c == '.') {
    c = (this->current_ = p) < this->end_ ? *p++ : '\0';
    while (c >= '0' && c <= '9')
      c = (this->current_ = p) < this->end_ ? *p++ : '\0';
  }
  // exponential part
  if (c == 'e' || c == 'E') {
    c = (this->current_ = p) < this->end_ ? *p++ : '\0';
    if (c == '+' || c == '-')
      c = (this->current_ = p) < this->end_ ? *p++ : '\0';
    while (c >= '0' && c <= '9')
      c = (this->current_ = p) < this->end_ ? *p++ : '\0';
  }
}

bool Reader::readString()
{
  Char c = '\0';
  while (this->current_ != this->end_) {
    c = this->getNextChar();
    if (c == '\\')
      this->getNextChar();
    else if (c == '"')
      break;
  }
  return c == '"';
}

bool Reader::readObject(Token& tokenStart)
{
  Token tokenName;
  JSONCPP_STRING name;
  Value init(objectValue);
  this->currentValue().swapPayload(init);
  this->currentValue().setOffsetStart(tokenStart.start_ - this->begin_);
  while (this->readToken(tokenName)) {
    bool initialTokenOk = true;
    while (tokenName.type_ == tokenComment && initialTokenOk)
      initialTokenOk = this->readToken(tokenName);
    if (!initialTokenOk)
      break;
    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
      return true;
    name.clear();
    if (tokenName.type_ == tokenString) {
      if (!this->decodeString(tokenName, name))
        return this->recoverFromError(tokenObjectEnd);
    } else if (tokenName.type_ == tokenNumber &&
               this->features_.allowNumericKeys_) {
      Value numberName;
      if (!this->decodeNumber(tokenName, numberName))
        return this->recoverFromError(tokenObjectEnd);
      name = JSONCPP_STRING(numberName.asCString());
    } else {
      break;
    }

    Token colon;
    if (!this->readToken(colon) || colon.type_ != tokenMemberSeparator) {
      return this->addErrorAndRecover("Missing ':' after object member name",
                                      colon, tokenObjectEnd);
    }
    Value& value = this->currentValue()[name];
    this->nodes_.push(&value);
    bool ok = this->readValue();
    this->nodes_.pop();
    if (!ok) // error already set
      return this->recoverFromError(tokenObjectEnd);

    Token comma;
    if (!this->readToken(comma) ||
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
         comma.type_ != tokenComment)) {
      return this->addErrorAndRecover(
        "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
    }
    bool finalizeTokenOk = true;
    while (comma.type_ == tokenComment && finalizeTokenOk)
      finalizeTokenOk = this->readToken(comma);
    if (comma.type_ == tokenObjectEnd)
      return true;
  }
  return this->addErrorAndRecover("Missing '}' or object member name",
                                  tokenName, tokenObjectEnd);
}

bool Reader::readArray(Token& tokenStart)
{
  Value init(arrayValue);
  this->currentValue().swapPayload(init);
  this->currentValue().setOffsetStart(tokenStart.start_ - this->begin_);
  this->skipSpaces();
  if (this->current_ != this->end_ && *this->current_ == ']') // empty array
  {
    Token endArray;
    this->readToken(endArray);
    return true;
  }
  int index = 0;
  for (;;) {
    Value& value = this->currentValue()[index++];
    this->nodes_.push(&value);
    bool ok = this->readValue();
    this->nodes_.pop();
    if (!ok) // error already set
      return this->recoverFromError(tokenArrayEnd);

    Token token;
    // Accept Comment after last item in the array.
    ok = this->readToken(token);
    while (token.type_ == tokenComment && ok) {
      ok = this->readToken(token);
    }
    bool badTokenType =
      (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
    if (!ok || badTokenType) {
      return this->addErrorAndRecover(
        "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
    }
    if (token.type_ == tokenArrayEnd)
      break;
  }
  return true;
}

bool Reader::decodeNumber(Token& token)
{
  Value decoded;
  if (!this->decodeNumber(token, decoded))
    return false;
  this->currentValue().swapPayload(decoded);
  this->currentValue().setOffsetStart(token.start_ - this->begin_);
  this->currentValue().setOffsetLimit(token.end_ - this->begin_);
  return true;
}

bool Reader::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_;
  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::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 this->decodeDouble(token, decoded);
    Value::UInt 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
      // 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 > maxIntegerValue % 10) {
        return this->decodeDouble(token, decoded);
      }
    }
    value = value * 10 + digit;
  }
  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);
  else
    decoded = value;
  return true;
}

bool Reader::decodeDouble(Token& token)
{
  Value decoded;
  if (!this->decodeDouble(token, decoded))
    return false;
  this->currentValue().swapPayload(decoded);
  this->currentValue().setOffsetStart(token.start_ - this->begin_);
  this->currentValue().setOffsetLimit(token.end_ - this->begin_);
  return true;
}

bool Reader::decodeDouble(Token& token, Value& decoded)
{
  double value = 0;
  JSONCPP_STRING buffer(token.start_, token.end_);
  JSONCPP_ISTRINGSTREAM is(buffer);
  if (!(is >> value))
    return this->addError("'" + JSONCPP_STRING(token.start_, token.end_) +
                            "' is not a number.",
                          token);
  decoded = value;
  return true;
}

bool Reader::decodeString(Token& token)
{
  JSONCPP_STRING decoded_string;
  if (!this->decodeString(token, decoded_string))
    return false;
  Value decoded(decoded_string);
  this->currentValue().swapPayload(decoded);
  this->currentValue().setOffsetStart(token.start_ - this->begin_);
  this->currentValue().setOffsetLimit(token.end_ - this->begin_);
  return true;
}

bool Reader::decodeString(Token& token, JSONCPP_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 (current == end)
        return this->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 (!this->decodeUnicodeCodePoint(token, current, end, unicode))
            return false;
          decoded += codePointToUTF8(unicode);
        } break;
        default:
          return this->addError("Bad escape sequence in string", token,
                                current);
      }
    } else {
      decoded += c;
    }
  }
  return true;
}

bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
                                    Location end, unsigned int& unicode)
{

  if (!this->decodeUnicodeEscapeSequence(token, current, end, unicode))
    return false;
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
    // surrogate pairs
    if (end - current < 6)
      return this->addError(
        "additional six characters expected to parse unicode surrogate pair.",
        token, current);
    unsigned int surrogatePair;
    if (*(current++) == '\\' && *(current++) == 'u') {
      if (this->decodeUnicodeEscapeSequence(token, current, end,
                                            surrogatePair)) {
        unicode =
          0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
      } else
        return false;
    } else
      return this->addError(
        "expecting another \\u token to begin the second half of "
        "a unicode surrogate pair",
        token, current);
  }
  return true;
}

bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
                                         Location end,
                                         unsigned int& ret_unicode)
{
  if (end - current < 4)
    return this->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 this->addError(
        "Bad unicode escape sequence in string: hexadecimal digit expected.",
        token, current);
  }
  ret_unicode = static_cast<unsigned int>(unicode);
  return true;
}

bool Reader::addError(const JSONCPP_STRING& message, Token& token,
                      Location extra)
{
  ErrorInfo info;
  info.token_ = token;
  info.message_ = message;
  info.extra_ = extra;
  this->errors_.push_back(info);
  return false;
}

bool Reader::recoverFromError(TokenType skipUntilToken)
{
  size_t const errorCount = this->errors_.size();
  Token skip;
  for (;;) {
    if (!this->readToken(skip))
      this->errors_.resize(errorCount); // discard errors caused by recovery
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
      break;
  }
  this->errors_.resize(errorCount);
  return false;
}

bool Reader::addErrorAndRecover(const JSONCPP_STRING& message, Token& token,
                                TokenType skipUntilToken)
{
  this->addError(message, token);
  return this->recoverFromError(skipUntilToken);
}

Value& Reader::currentValue()
{
  return *(this->nodes_.top());
}

Reader::Char Reader::getNextChar()
{
  if (this->current_ == this->end_)
    return 0;
  return *this->current_++;
}

void Reader::getLocationLineAndColumn(Location location, int& line,
                                      int& column) const
{
  Location current = this->begin_;
  Location lastLineStart = current;
  line = 0;
  while (current < location && current != this->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;
}

JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const
{
  int line, column;
  this->getLocationLineAndColumn(location, line, column);
  char buffer[18 + 16 + 16 + 1];
  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
  return buffer;
}

// Deprecated. Preserved for backward compatibility
JSONCPP_STRING Reader::getFormatedErrorMessages() const
{
  return this->getFormattedErrorMessages();
}

JSONCPP_STRING Reader::getFormattedErrorMessages() const
{
  JSONCPP_STRING formattedMessage;
  for (Errors::const_iterator itError = this->errors_.begin();
       itError != this->errors_.end(); ++itError) {
    const ErrorInfo& error = *itError;
    formattedMessage +=
      "* " + this->getLocationLineAndColumn(error.token_.start_) + "\n";
    formattedMessage += "  " + error.message_ + "\n";
    if (error.extra_)
      formattedMessage += "See " +
        this->getLocationLineAndColumn(error.extra_) + " for detail.\n";
  }
  return formattedMessage;
}

std::vector<Reader::StructuredError> Reader::getStructuredErrors() const
{
  std::vector<Reader::StructuredError> allErrors;
  for (Errors::const_iterator itError = this->errors_.begin();
       itError != this->errors_.end(); ++itError) {
    const ErrorInfo& error = *itError;
    Reader::StructuredError structured;
    structured.offset_start = error.token_.start_ - this->begin_;
    structured.offset_limit = error.token_.end_ - this->begin_;
    structured.message = error.message_;
    allErrors.push_back(structured);
  }
  return allErrors;
}

bool Reader::pushError(const Value& value, const JSONCPP_STRING& message)
{
  ptrdiff_t const length = this->end_ - this->begin_;
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
    return false;
  Token token;
  token.type_ = tokenError;
  token.start_ = this->begin_ + value.getOffsetStart();
  token.end_ = this->end_ + value.getOffsetLimit();
  ErrorInfo info;
  info.token_ = token;
  info.message_ = message;
  info.extra_ = 0;
  this->errors_.push_back(info);
  return true;
}

bool Reader::pushError(const Value& value, const JSONCPP_STRING& message,
                       const Value& extra)
{
  ptrdiff_t const length = this->end_ - this->begin_;
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
      extra.getOffsetLimit() > length)
    return false;
  Token token;
  token.type_ = tokenError;
  token.start_ = this->begin_ + value.getOffsetStart();
  token.end_ = this->begin_ + value.getOffsetLimit();
  ErrorInfo info;
  info.token_ = token;
  info.message_ = message;
  info.extra_ = this->begin_ + extra.getOffsetStart();
  this->errors_.push_back(info);
  return true;
}

bool Reader::good() const
{
  return !this->errors_.size();
}

// exact copy of Features
class OurFeatures
{
public:
  static OurFeatures all();
  bool allowComments_;
  bool strictRoot_;
  bool allowDroppedNullPlaceholders_;
  bool allowNumericKeys_;
  bool allowSingleQuotes_;
  bool failIfExtra_;
  bool rejectDupKeys_;
  bool allowSpecialFloats_;
  int stackLimit_;
}; // OurFeatures

// exact copy of Implementation of class Features
// ////////////////////////////////

OurFeatures OurFeatures::all()
{
  return OurFeatures();
}

// Implementation of class Reader
// ////////////////////////////////

// exact copy of Reader, renamed to OurReader
class OurReader
{
public:
  typedef char Char;
  typedef const Char* Location;
  struct StructuredError
  {
    ptrdiff_t offset_start;
    ptrdiff_t offset_limit;
    JSONCPP_STRING message;
  };

  OurReader(OurFeatures const& features);
  bool parse(const char* beginDoc, const char* endDoc, Value& root,
             bool collectComments = true);
  JSONCPP_STRING getFormattedErrorMessages() const;
  std::vector<StructuredError> getStructuredErrors() const;
  bool pushError(const Value& value, const JSONCPP_STRING& message);
  bool pushError(const Value& value, const JSONCPP_STRING& message,
                 const Value& extra);
  bool good() 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_;
    JSONCPP_STRING message_;
    Location extra_;
  };

  typedef std::deque<ErrorInfo> Errors;

  bool readToken(Token& token);
  void skipSpaces();
  bool match(Location pattern, int patternLength);
  bool readComment();
  bool readCStyleComment();
  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, JSONCPP_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 JSONCPP_STRING& message, Token& token,
                Location extra = 0);
  bool recoverFromError(TokenType skipUntilToken);
  bool addErrorAndRecover(const JSONCPP_STRING& message, Token& token,
                          TokenType skipUntilToken);
  void skipUntilSpace();
  Value& currentValue();
  Char getNextChar();
  void getLocationLineAndColumn(Location location, int& line,
                                int& column) const;
  JSONCPP_STRING getLocationLineAndColumn(Location location) const;
  void addComment(Location begin, Location end, CommentPlacement placement);
  void skipCommentTokens(Token& token);

  static JSONCPP_STRING normalizeEOL(Location begin, Location end);
  static bool containsNewLine(Location begin, Location end);

  typedef std::stack<Value*> Nodes;
  Nodes nodes_;
  Errors errors_;
  JSONCPP_STRING document_;
  Location begin_;
  Location end_;
  Location current_;
  Location lastValueEnd_;
  Value* lastValue_;
  JSONCPP_STRING commentsBefore_;

  OurFeatures const features_;
  bool collectComments_;
}; // OurReader

// complete copy of Read impl, for OurReader

bool OurReader::containsNewLine(OurReader::Location begin,
                                OurReader::Location end)
{
  for (; begin < end; ++begin)
    if (*begin == '\n' || *begin == '\r')
      return true;
  return false;
}

OurReader::OurReader(OurFeatures const& features)
  : errors_()
  , document_()
  , begin_()
  , end_()
  , current_()
  , lastValueEnd_()
  , lastValue_()
  , commentsBefore_()
  , features_(features)
  , collectComments_()
{
}

bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
                      bool collectComments)
{
  if (!this->features_.allowComments_) {
    collectComments = false;
  }

  this->begin_ = beginDoc;
  this->end_ = endDoc;
  this->collectComments_ = collectComments;
  this->current_ = this->begin_;
  this->lastValueEnd_ = 0;
  this->lastValue_ = 0;
  this->commentsBefore_.clear();
  this->errors_.clear();
  while (!this->nodes_.empty())
    this->nodes_.pop();
  this->nodes_.push(&root);

  bool successful = this->readValue();
  Token token;
  this->skipCommentTokens(token);
  if (this->features_.failIfExtra_) {
    if ((this->features_.strictRoot_ || token.type_ != tokenError) &&
        token.type_ != tokenEndOfStream) {
      this->addError("Extra non-whitespace after JSON value.", token);
      return false;
    }
  }
  if (this->collectComments_ && !this->commentsBefore_.empty())
    root.setComment(this->commentsBefore_, commentAfter);
  if (this->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;
      this->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 (static_cast<int>(this->nodes_.size()) > this->features_.stackLimit_)
    throwRuntimeError("Exceeded stackLimit in readValue().");
  Token token;
  this->skipCommentTokens(token);
  bool successful = true;

  if (this->collectComments_ && !this->commentsBefore_.empty()) {
    this->currentValue().setComment(this->commentsBefore_, commentBefore);
    this->commentsBefore_.clear();
  }

  switch (token.type_) {
    case tokenObjectBegin:
      successful = this->readObject(token);
      this->currentValue().setOffsetLimit(this->current_ - this->begin_);
      break;
    case tokenArrayBegin:
      successful = this->readArray(token);
      this->currentValue().setOffsetLimit(this->current_ - this->begin_);
      break;
    case tokenNumber:
      successful = this->decodeNumber(token);
      break;
    case tokenString:
      successful = this->decodeString(token);
      break;
    case tokenTrue: {
      Value v(true);
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenFalse: {
      Value v(false);
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenNull: {
      Value v;
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenNaN: {
      Value v(std::numeric_limits<double>::quiet_NaN());
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenPosInf: {
      Value v(std::numeric_limits<double>::infinity());
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenNegInf: {
      Value v(-std::numeric_limits<double>::infinity());
      this->currentValue().swapPayload(v);
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
    } break;
    case tokenArraySeparator:
    case tokenObjectEnd:
    case tokenArrayEnd:
      if (this->features_.allowDroppedNullPlaceholders_) {
        // "Un-read" the current token and mark the current value as a null
        // token.
        this->current_--;
        Value v;
        this->currentValue().swapPayload(v);
        this->currentValue().setOffsetStart(this->current_ - this->begin_ - 1);
        this->currentValue().setOffsetLimit(this->current_ - this->begin_);
        break;
      } // else, fall through ...
    default:
      this->currentValue().setOffsetStart(token.start_ - this->begin_);
      this->currentValue().setOffsetLimit(token.end_ - this->begin_);
      return this->addError("Syntax error: value, object or array expected.",
                            token);
  }

  if (this->collectComments_) {
    this->lastValueEnd_ = this->current_;
    this->lastValue_ = &this->currentValue();
  }

  return successful;
}

void OurReader::skipCommentTokens(Token& token)
{
  if (this->features_.allowComments_) {
    do {
      this->readToken(token);
    } while (token.type_ == tokenComment);
  } else {
    this->readToken(token);
  }
}

bool OurReader::readToken(Token& token)
{
  this->skipSpaces();
  token.start_ = this->current_;
  Char c = this->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 = this->readString();
      break;
    case '\'':
      if (this->features_.allowSingleQuotes_) {
        token.type_ = tokenString;
        ok = this->readStringSingleQuote();
        break;
      } // else continue
    case '/':
      token.type_ = tokenComment;
      ok = this->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;
      this->readNumber(false);
      break;
    case '-':
      if (this->readNumber(true)) {
        token.type_ = tokenNumber;
      } else {
        token.type_ = tokenNegInf;
        ok = this->features_.allowSpecialFloats_ && this->match("nfinity", 7);
      }
      break;
    case 't':
      token.type_ = tokenTrue;
      ok = this->match("rue", 3);
      break;
    case 'f':
      token.type_ = tokenFalse;
      ok = this->match("alse", 4);
      break;
    case 'n':
      token.type_ = tokenNull;
      ok = this->match("ull", 3);
      break;
    case 'N':
      if (this->features_.allowSpecialFloats_) {
        token.type_ = tokenNaN;
        ok = this->match("aN", 2);
      } else {
        ok = false;
      }
      break;
    case 'I':
      if (this->features_.allowSpecialFloats_) {
        token.type_ = tokenPosInf;
        ok = this->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_ = this->current_;
  return true;
}

void OurReader::skipSpaces()
{
  while (this->current_ != this->end_) {
    Char c = *this->current_;
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
      ++this->current_;
    else
      break;
  }
}

bool OurReader::match(Location pattern, int patternLength)
{
  if (this->end_ - this->current_ < patternLength)
    return false;
  int index = patternLength;
  while (index--)
    if (this->current_[index] != pattern[index])
      return false;
  this->current_ += patternLength;
  return true;
}

bool OurReader::readComment()
{
  Location commentBegin = this->current_ - 1;
  Char c = this->getNextChar();
  bool successful = false;
  if (c == '*')
    successful = this->readCStyleComment();
  else if (c == '/')
    successful = this->readCppStyleComment();
  if (!successful)
    return false;

  if (this->collectComments_) {
    CommentPlacement placement = commentBefore;
    if (this->lastValueEnd_ &&
        !containsNewLine(this->lastValueEnd_, commentBegin)) {
      if (c != '*' || !containsNewLine(commentBegin, this->current_))
        placement = commentAfterOnSameLine;
    }

    this->addComment(commentBegin, this->current_, placement);
  }
  return true;
}

JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin,
                                       OurReader::Location end)
{
  JSONCPP_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(this->collectComments_);
  const JSONCPP_STRING& normalized = normalizeEOL(begin, end);
  if (placement == commentAfterOnSameLine) {
    assert(this->lastValue_ != 0);
    this->lastValue_->setComment(normalized, placement);
  } else {
    this->commentsBefore_ += normalized;
  }
}

bool OurReader::readCStyleComment()
{
  while ((this->current_ + 1) < this->end_) {
    Char c = this->getNextChar();
    if (c == '*' && *this->current_ == '/')
      break;
  }
  return this->getNextChar() == '/';
}

bool OurReader::readCppStyleComment()
{
  while (this->current_ != this->end_) {
    Char c = this->getNextChar();
    if (c == '\n')
      break;
    if (c == '\r') {
      // Consume DOS EOL. It will be normalized in addComment.
      if (this->current_ != this->end_ && *this->current_ == '\n')
        this->getNextChar();
      // Break on Moc OS 9 EOL.
      break;
    }
  }
  return true;
}

bool OurReader::readNumber(bool checkInf)
{
  const char* p = this->current_;
  if (checkInf && p != this->end_ && *p == 'I') {
    this->current_ = ++p;
    return false;
  }
  char c = '0'; // stopgap for already consumed character
  // integral part
  while (c >= '0' && c <= '9')
    c = (this->current_ = p) < this->end_ ? *p++ : '\0';
  // fractional part
  if (c == '.') {
    c = (this->current_ = p) < this->end_ ? *p++ : '\0';
    while (c >= '0' && c <= '9')
      c = (this->current_ = p) < this->end_ ? *p++ : '\0';
  }
  // exponential part
  if (c == 'e' || c == 'E') {
    c = (this->current_ = p) < this->end_ ? *p++ : '\0';
    if (c == '+' || c == '-')
      c = (this->current_ = p) < this->end_ ? *p++ : '\0';
    while (c >= '0' && c <= '9')
      c = (this->current_ = p) < this->end_ ? *p++ : '\0';
  }
  return true;
}
bool OurReader::readString()
{
  Char c = 0;
  while (this->current_ != this->end_) {
    c = this->getNextChar();
    if (c == '\\')
      this->getNextChar();
    else if (c == '"')
      break;
  }
  return c == '"';
}

bool OurReader::readStringSingleQuote()
{
  Char c = 0;
  while (this->current_ != this->end_) {
    c = this->getNextChar();
    if (c == '\\')
      this->getNextChar();
    else if (c == '\'')
      break;
  }
  return c == '\'';
}

bool OurReader::readObject(Token& tokenStart)
{
  Token tokenName;
  JSONCPP_STRING name;
  Value init(objectValue);
  this->currentValue().swapPayload(init);
  this->currentValue().setOffsetStart(tokenStart.start_ - this->begin_);
  while (this->readToken(tokenName)) {
    bool initialTokenOk = true;
    while (tokenName.type_ == tokenComment && initialTokenOk)
      initialTokenOk = this->readToken(tokenName);
    if (!initialTokenOk)
      break;
    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
      return true;
    name.clear();
    if (tokenName.type_ == tokenString) {
      if (!this->decodeString(tokenName, name))
        return this->recoverFromError(tokenObjectEnd);
    } else if (tokenName.type_ == tokenNumber &&
               this->features_.allowNumericKeys_) {
      Value numberName;
      if (!this->decodeNumber(tokenName, numberName))
        return this->recoverFromError(tokenObjectEnd);
      name = numberName.asString();
    } else {
      break;
    }

    Token colon;
    if (!this->readToken(colon) || colon.type_ != tokenMemberSeparator) {
      return this->addErrorAndRecover("Missing ':' after object member name",
                                      colon, tokenObjectEnd);
    }
    if (name.length() >= (1U << 30))
      throwRuntimeError("keylength >= 2^30");
    if (this->features_.rejectDupKeys_ &&
        this->currentValue().isMember(name)) {
      JSONCPP_STRING msg = "Duplicate key: '" + name + "'";
      return this->addErrorAndRecover(msg, tokenName, tokenObjectEnd);
    }
    Value& value = this->currentValue()[name];
    this->nodes_.push(&value);
    bool ok = this->readValue();
    this->nodes_.pop();
    if (!ok) // error already set
      return this->recoverFromError(tokenObjectEnd);

    Token comma;
    if (!this->readToken(comma) ||
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
         comma.type_ != tokenComment)) {
      return this->addErrorAndRecover(
        "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
    }
    bool finalizeTokenOk = true;
    while (comma.type_ == tokenComment && finalizeTokenOk)
      finalizeTokenOk = this->readToken(comma);
    if (comma.type_ == tokenObjectEnd)
      return true;
  }
  return this->addErrorAndRecover("Missing '}' or object member name",
                                  tokenName, tokenObjectEnd);
}

bool OurReader::readArray(Token& tokenStart)
{
  Value init(arrayValue);
  this->currentValue().swapPayload(init);
  this->currentValue().setOffsetStart(tokenStart.start_ - this->begin_);
  this->skipSpaces();
  if (this->current_ != this->end_ && *this->current_ == ']') // empty array
  {
    Token endArray;
    this->readToken(endArray);
    return true;
  }
  int index = 0;
  for (;;) {
    Value& value = this->currentValue()[index++];
    this->nodes_.push(&value);
    bool ok = this->readValue();
    this->nodes_.pop();
    if (!ok) // error already set
      return this->recoverFromError(tokenArrayEnd);

    Token token;
    // Accept Comment after last item in the array.
    ok = this->readToken(token);
    while (token.type_ == tokenComment && ok) {
      ok = this->readToken(token);
    }
    bool badTokenType =
      (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
    if (!ok || badTokenType) {
      return this->addErrorAndRecover(
        "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
    }
    if (token.type_ == tokenArrayEnd)
      break;
  }
  return true;
}

bool OurReader::decodeNumber(Token& token)
{
  Value decoded;
  if (!this->decodeNumber(token, decoded))
    return false;
  this->currentValue().swapPayload(decoded);
  this->currentValue().setOffsetStart(token.start_ - this->begin_);
  this->currentValue().setOffsetLimit(token.end_ - this->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_;
  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)
    : Value::maxLargestUInt;
  Value::LargestUInt threshold = maxIntegerValue / 10;
  Value::LargestUInt value = 0;
  while (current < token.end_) {
    Char c = *current++;
    if (c < '0' || c > '9')
      return this->decodeDouble(token, decoded);
    Value::UInt 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
      // 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 > maxIntegerValue % 10) {
        return this->decodeDouble(token, decoded);
      }
    }
    value = value * 10 + digit;
  }
  if (isNegative)
    decoded = -Value::LargestInt(value);
  else if (value <= Value::LargestUInt(Value::maxInt))
    decoded = Value::LargestInt(value);
  else
    decoded = value;
  return true;
}

bool OurReader::decodeDouble(Token& token)
{
  Value decoded;
  if (!this->decodeDouble(token, decoded))
    return false;
  this->currentValue().swapPayload(decoded);
  this->currentValue().setOffsetStart(token.start_ - this->begin_);
  this->currentValue().setOffsetLimit(token.end_ - this->begin_);
  return true;
}

bool OurReader::decodeDouble(Token& token, Value& decoded)
{
  double value = 0;
  const int bufferSize = 32;
  int count;
  ptrdiff_t const length = token.end_ - token.start_;

  // Sanity check to avoid buffer overflow exploits.
  if (length < 0) {
    return this->addError("Unable to parse token length", token);
  }
  size_t const ulength = static_cast<size_t>(length);

  // 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_, ulength);
    buffer[length] = 0;
    fixNumericLocaleInput(buffer, buffer + length);
    count = sscanf(buffer, format, &value);
  } else {
    JSONCPP_STRING buffer(token.start_, token.end_);
    count = sscanf(buffer.c_str(), format, &value);
  }

  if (count != 1)
    return this->addError("'" + JSONCPP_STRING(token.start_, token.end_) +
                            "' is not a number.",
                          token);
  decoded = value;
  return true;
}

bool OurReader::decodeString(Token& token)
{
  JSONCPP_STRING decoded_string;
  if (!this->decodeString(token, decoded_string))
    return false;
  Value decoded(decoded_string);
  this->currentValue().swapPayload(decoded);
  this->currentValue().setOffsetStart(token.start_ - this->begin_);
  this->currentValue().setOffsetLimit(token.end_ - this->begin_);
  return true;
}

bool OurReader::decodeString(Token& token, JSONCPP_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 (current == end)
        return this->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 (!this->decodeUnicodeCodePoint(token, current, end, unicode))
            return false;
          decoded += codePointToUTF8(unicode);
        } break;
        default:
          return this->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)
{

  unicode = 0; // Convince scanbuild this is always initialized before use.
  if (!this->decodeUnicodeEscapeSequence(token, current, end, unicode))
    return false;
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
    // surrogate pairs
    if (end - current < 6)
      return this->addError(
        "additional six characters expected to parse unicode surrogate pair.",
        token, current);
    unsigned int surrogatePair;
    if (*(current++) == '\\' && *(current++) == 'u') {
      if (this->decodeUnicodeEscapeSequence(token, current, end,
                                            surrogatePair)) {
        unicode =
          0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
      } else
        return false;
    } else
      return this->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 this->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 this->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 JSONCPP_STRING& message, Token& token,
                         Location extra)
{
  ErrorInfo info;
  info.token_ = token;
  info.message_ = message;
  info.extra_ = extra;
  this->errors_.push_back(info);
  return false;
}

bool OurReader::recoverFromError(TokenType skipUntilToken)
{
  size_t errorCount = this->errors_.size();
  Token skip;
  for (;;) {
    if (!this->readToken(skip))
      this->errors_.resize(errorCount); // discard errors caused by recovery
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
      break;
  }
  this->errors_.resize(errorCount);
  return false;
}

bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, Token& token,
                                   TokenType skipUntilToken)
{
  this->addError(message, token);
  return this->recoverFromError(skipUntilToken);
}

Value& OurReader::currentValue()
{
  return *(this->nodes_.top());
}

OurReader::Char OurReader::getNextChar()
{
  if (this->current_ == this->end_)
    return 0;
  return *this->current_++;
}

void OurReader::getLocationLineAndColumn(Location location, int& line,
                                         int& column) const
{
  Location current = this->begin_;
  Location lastLineStart = current;
  line = 0;
  while (current < location && current != this->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;
}

JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const
{
  int line, column;
  this->getLocationLineAndColumn(location, line, column);
  char buffer[18 + 16 + 16 + 1];
  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
  return buffer;
}

JSONCPP_STRING OurReader::getFormattedErrorMessages() const
{
  JSONCPP_STRING formattedMessage;
  for (Errors::const_iterator itError = this->errors_.begin();
       itError != this->errors_.end(); ++itError) {
    const ErrorInfo& error = *itError;
    formattedMessage +=
      "* " + this->getLocationLineAndColumn(error.token_.start_) + "\n";
    formattedMessage += "  " + error.message_ + "\n";
    if (error.extra_)
      formattedMessage += "See " +
        this->getLocationLineAndColumn(error.extra_) + " for detail.\n";
  }
  return formattedMessage;
}

std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const
{
  std::vector<OurReader::StructuredError> allErrors;
  for (Errors::const_iterator itError = this->errors_.begin();
       itError != this->errors_.end(); ++itError) {
    const ErrorInfo& error = *itError;
    OurReader::StructuredError structured;
    structured.offset_start = error.token_.start_ - this->begin_;
    structured.offset_limit = error.token_.end_ - this->begin_;
    structured.message = error.message_;
    allErrors.push_back(structured);
  }
  return allErrors;
}

bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message)
{
  ptrdiff_t length = this->end_ - this->begin_;
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
    return false;
  Token token;
  token.type_ = tokenError;
  token.start_ = this->begin_ + value.getOffsetStart();
  token.end_ = this->end_ + value.getOffsetLimit();
  ErrorInfo info;
  info.token_ = token;
  info.message_ = message;
  info.extra_ = 0;
  this->errors_.push_back(info);
  return true;
}

bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message,
                          const Value& extra)
{
  ptrdiff_t length = this->end_ - this->begin_;
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
      extra.getOffsetLimit() > length)
    return false;
  Token token;
  token.type_ = tokenError;
  token.start_ = this->begin_ + value.getOffsetStart();
  token.end_ = this->begin_ + value.getOffsetLimit();
  ErrorInfo info;
  info.token_ = token;
  info.message_ = message;
  info.extra_ = this->begin_ + extra.getOffsetStart();
  this->errors_.push_back(info);
  return true;
}

bool OurReader::good() const
{
  return !this->errors_.size();
}

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,
             JSONCPP_STRING* errs) JSONCPP_OVERRIDE
  {
    bool ok =
      this->reader_.parse(beginDoc, endDoc, *root, this->collectComments_);
    if (errs) {
      *errs = this->reader_.getFormattedErrorMessages();
    }
    return ok;
  }
};

CharReaderBuilder::CharReaderBuilder()
{
  setDefaults(&this->settings_);
}
CharReaderBuilder::~CharReaderBuilder()
{
}
CharReader* CharReaderBuilder::newCharReader() const
{
  bool collectComments = this->settings_["collectComments"].asBool();
  OurFeatures features = OurFeatures::all();
  features.allowComments_ = this->settings_["allowComments"].asBool();
  features.strictRoot_ = this->settings_["strictRoot"].asBool();
  features.allowDroppedNullPlaceholders_ =
    this->settings_["allowDroppedNullPlaceholders"].asBool();
  features.allowNumericKeys_ = this->settings_["allowNumericKeys"].asBool();
  features.allowSingleQuotes_ = this->settings_["allowSingleQuotes"].asBool();
  features.stackLimit_ = this->settings_["stackLimit"].asInt();
  features.failIfExtra_ = this->settings_["failIfExtra"].asBool();
  features.rejectDupKeys_ = this->settings_["rejectDupKeys"].asBool();
  features.allowSpecialFloats_ =
    this->settings_["allowSpecialFloats"].asBool();
  return new OurCharReader(collectComments, features);
}
static void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys)
{
  valid_keys->clear();
  valid_keys->insert("collectComments");
  valid_keys->insert("allowComments");
  valid_keys->insert("strictRoot");
  valid_keys->insert("allowDroppedNullPlaceholders");
  valid_keys->insert("allowNumericKeys");
  valid_keys->insert("allowSingleQuotes");
  valid_keys->insert("stackLimit");
  valid_keys->insert("failIfExtra");
  valid_keys->insert("rejectDupKeys");
  valid_keys->insert("allowSpecialFloats");
}
bool CharReaderBuilder::validate(Json::Value* invalid) const
{
  Json::Value my_invalid;
  if (!invalid)
    invalid = &my_invalid; // so we do not need to test for NULL
  Json::Value& inv = *invalid;
  std::set<JSONCPP_STRING> valid_keys;
  getValidReaderKeys(&valid_keys);
  Value::Members keys = this->settings_.getMemberNames();
  size_t n = keys.size();
  for (size_t i = 0; i < n; ++i) {
    JSONCPP_STRING const& key = keys[i];
    if (valid_keys.find(key) == valid_keys.end()) {
      inv[key] = this->settings_[key];
    }
  }
  return 0u == inv.size();
}
Value& CharReaderBuilder::operator[](JSONCPP_STRING key)
{
  return this->settings_[key];
}
// static
void CharReaderBuilder::strictMode(Json::Value* settings)
{
  //! [CharReaderBuilderStrictMode]
  (*settings)["allowComments"] = 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;
  //! [CharReaderBuilderStrictMode]
}
// static
void CharReaderBuilder::setDefaults(Json::Value* settings)
{
  //! [CharReaderBuilderDefaults]
  (*settings)["collectComments"] = true;
  (*settings)["allowComments"] = 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;
  //! [CharReaderBuilderDefaults]
}

//////////////////////////////////
// global functions

bool parseFromStream(CharReader::Factory const& fact, JSONCPP_ISTREAM& sin,
                     Value* root, JSONCPP_STRING* errs)
{
  JSONCPP_OSTRINGSTREAM ssin;
  ssin << sin.rdbuf();
  JSONCPP_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);
}

JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root)
{
  CharReaderBuilder b;
  JSONCPP_STRING errs;
  bool ok = parseFromStream(b, sin, &root, &errs);
  if (!ok) {
    fprintf(stderr, "Error from reader: %s", errs.c_str());

    throwRuntimeError(errs);
  }
  return sin;
}

} // namespace Json
