text: // Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
  // 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/value.h>
  #include <json/writer.h>
  #endif // if !defined(JSON_IS_AMALGAMATION)
  #include <cassert>
  #include <cmath>
  #include <cstring>
  #include <sstream>
  #include <utility>
  #ifdef JSON_USE_CPPTL
  #include <cpptl/conststring.h>
  #endif
  #include <algorithm> // min()
  #include <cstddef>   // size_t
  
  // Provide implementation equivalent of std::snprintf for older _MSC compilers
  #if defined(_MSC_VER) && _MSC_VER < 1900
  #include <stdarg.h>
  static int msvc_pre1900_c99_vsnprintf(char* outBuf,
                                        size_t size,
                                        const char* format,
                                        va_list ap) {
    int count = -1;
    if (size != 0)
      count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
    if (count == -1)
      count = _vscprintf(format, ap);
    return count;
  }
  
  int JSON_API msvc_pre1900_c99_snprintf(char* outBuf,
                                         size_t size,
                                         const char* format,
                                         ...) {
    va_list ap;
    va_start(ap, format);
    const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
    va_end(ap);
    return count;
  }
  #endif
  
  // Disable warning C4702 : unreachable code
  #if defined(_MSC_VER)
  #pragma warning(disable : 4702)
  #endif
  
  #define JSON_ASSERT_UNREACHABLE assert(false)
  
  namespace Json {
  
  template <typename T>
  static std::unique_ptr<T> cloneUnique(const std::unique_ptr<T>& p) {
    std::unique_ptr<T> r;
    if (p) {
      r = std::unique_ptr<T>(new T(*p));
    }
    return r;
  }
  
  // This is a walkaround to avoid the static initialization of Value::null.
  // kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of
  // 8 (instead of 4) as a bit of future-proofing.
  #if defined(__ARMEL__)
  #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
  #else
  #define ALIGNAS(byte_alignment)
  #endif
  // static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
  // const unsigned char& kNullRef = kNull[0];
  // const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
  // const Value& Value::nullRef = null;
  
  // static
  Value const& Value::nullSingleton() {
    static Value const nullStatic;
    return nullStatic;
  }
  
  // for backwards compatibility, we'll leave these global references around, but
  // DO NOT use them in JSONCPP library code any more!
  Value const& Value::null = Value::nullSingleton();
  Value const& Value::nullRef = Value::nullSingleton();
  
  const Int Value::minInt = Int(~(UInt(-1) / 2));
  const Int Value::maxInt = Int(UInt(-1) / 2);
  const UInt Value::maxUInt = UInt(-1);
  #if defined(JSON_HAS_INT64)
  const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
  const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
  const UInt64 Value::maxUInt64 = UInt64(-1);
  // The constant is hard-coded because some compiler have trouble
  // converting Value::maxUInt64 to a double correctly (AIX/xlC).
  // Assumes that UInt64 is a 64 bits integer.
  static const double maxUInt64AsDouble = 18446744073709551615.0;
  #endif // defined(JSON_HAS_INT64)
  const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
  const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
  const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
  
  const UInt Value::defaultRealPrecision = 17;
  
  #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
  template <typename T, typename U>
  static inline bool InRange(double d, T min, U max) {
    // The casts can lose precision, but we are looking only for
    // an approximate range. Might fail on edge cases though. ~cdunn
    // return d >= static_cast<double>(min) && d <= static_cast<double>(max);
    return d >= min && d <= max;
  }
  #else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
  static inline double integerToDouble(Json::UInt64 value) {
    return static_cast<double>(Int64(value / 2)) * 2.0 +
           static_cast<double>(Int64(value & 1));
  }
  
  template <typename T> static inline double integerToDouble(T value) {
    return static_cast<double>(value);
  }
  
  template <typename T, typename U>
  static inline bool InRange(double d, T min, U max) {
    return d >= integerToDouble(min) && d <= integerToDouble(max);
  }
  #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
  
  /** Duplicates the specified string value.
   * @param value Pointer to the string to duplicate. Must be zero-terminated if
   *              length is "unknown".
   * @param length Length of the value. if equals to unknown, then it will be
   *               computed using strlen(value).
   * @return Pointer on the duplicate instance of string.
   */
  static inline char* duplicateStringValue(const char* value, size_t length) {
    // Avoid an integer overflow in the call to malloc below by limiting length
    // to a sane value.
    if (length >= static_cast<size_t>(Value::maxInt))
      length = Value::maxInt - 1;
  
    char* newString = static_cast<char*>(malloc(length + 1));
    if (newString == nullptr) {
      throwRuntimeError("in Json::Value::duplicateStringValue(): "
                        "Failed to allocate string value buffer");
    }
    memcpy(newString, value, length);
    newString[length] = 0;
    return newString;
  }
  
  /* Record the length as a prefix.
   */
  static inline char* duplicateAndPrefixStringValue(const char* value,
                                                    unsigned int length) {
    // Avoid an integer overflow in the call to malloc below by limiting length
    // to a sane value.
    JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
                                      sizeof(unsigned) - 1U,
                        "in Json::Value::duplicateAndPrefixStringValue(): "
                        "length too big for prefixing");
    unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
    char* newString = static_cast<char*>(malloc(actualLength));
    if (newString == nullptr) {
      throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
                        "Failed to allocate string value buffer");
    }
    *reinterpret_cast<unsigned*>(newString) = length;
    memcpy(newString + sizeof(unsigned), value, length);
    newString[actualLength - 1U] =
        0; // to avoid buffer over-run accidents by users later
    return newString;
  }
  inline static void decodePrefixedString(bool isPrefixed,
                                          char const* prefixed,
                                          unsigned* length,
                                          char const** value) {
    if (!isPrefixed) {
      *length = static_cast<unsigned>(strlen(prefixed));
      *value = prefixed;
    } else {
      *length = *reinterpret_cast<unsigned const*>(prefixed);
      *value = prefixed + sizeof(unsigned);
    }
  }
  /** Free the string duplicated by
   * duplicateStringValue()/duplicateAndPrefixStringValue().
   */
  #if JSONCPP_USING_SECURE_MEMORY
  static inline void releasePrefixedStringValue(char* value) {
    unsigned length = 0;
    char const* valueDecoded;
    decodePrefixedString(true, value, &length, &valueDecoded);
    size_t const size = sizeof(unsigned) + length + 1U;
    memset(value, 0, size);
    free(value);
  }
  static inline void releaseStringValue(char* value, unsigned length) {
    // length==0 => we allocated the strings memory
    size_t size = (length == 0) ? strlen(value) : length;
    memset(value, 0, size);
    free(value);
  }
  #else  // !JSONCPP_USING_SECURE_MEMORY
  static inline void releasePrefixedStringValue(char* value) { free(value); }
  static inline void releaseStringValue(char* value, unsigned) { free(value); }
  #endif // JSONCPP_USING_SECURE_MEMORY
  
  } // namespace Json
  
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // ValueInternals...
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  #if !defined(JSON_IS_AMALGAMATION)
  
  #include "json_valueiterator.inl"
  #endif // if !defined(JSON_IS_AMALGAMATION)
  
  namespace Json {
  
  Exception::Exception(String msg) : msg_(std::move(msg)) {}
  Exception::~Exception() JSONCPP_NOEXCEPT {}
  char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); }
  RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
  LogicError::LogicError(String const& msg) : Exception(msg) {}
  JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
    throw RuntimeError(msg);
  }
  JSONCPP_NORETURN void throwLogicError(String const& msg) {
    throw LogicError(msg);
  }
  
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // class Value::CZString
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  
  // Notes: policy_ indicates if the string was allocated when
  // a string is stored.
  
  Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
  
  Value::CZString::CZString(char const* str,
                            unsigned length,
                            DuplicationPolicy allocate)
      : cstr_(str) {
    // allocate != duplicate
    storage_.policy_ = allocate & 0x3;
    storage_.length_ = length & 0x3FFFFFFF;
  }
  
  Value::CZString::CZString(const CZString& other) {
    cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
                 ? duplicateStringValue(other.cstr_, other.storage_.length_)
                 : other.cstr_);
    storage_.policy_ =
        static_cast<unsigned>(
            other.cstr_
                ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
                           noDuplication
                       ? noDuplication
                       : duplicate)
                : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
        3U;
    storage_.length_ = other.storage_.length_;
  }
  
  Value::CZString::CZString(CZString&& other)
      : cstr_(other.cstr_), index_(other.index_) {
    other.cstr_ = nullptr;
  }
  
  Value::CZString::~CZString() {
    if (cstr_ && storage_.policy_ == duplicate) {
      releaseStringValue(const_cast<char*>(cstr_),
                         storage_.length_ + 1u); // +1 for null terminating
                                                 // character for sake of
                                                 // completeness but not actually
                                                 // necessary
    }
  }
  
  void Value::CZString::swap(CZString& other) {
    std::swap(cstr_, other.cstr_);
    std::swap(index_, other.index_);
  }
  
  Value::CZString& Value::CZString::operator=(const CZString& other) {
    cstr_ = other.cstr_;
    index_ = other.index_;
    return *this;
  }
  
  Value::CZString& Value::CZString::operator=(CZString&& other) {
    cstr_ = other.cstr_;
    index_ = other.index_;
    other.cstr_ = nullptr;
    return *this;
  }
  
  bool Value::CZString::operator<(const CZString& other) const {
    if (!cstr_)
      return index_ < other.index_;
    // return strcmp(cstr_, other.cstr_) < 0;
    // Assume both are strings.
    unsigned this_len = this->storage_.length_;
    unsigned other_len = other.storage_.length_;
    unsigned min_len = std::min<unsigned>(this_len, other_len);
    JSON_ASSERT(this->cstr_ && other.cstr_);
    int comp = memcmp(this->cstr_, other.cstr_, min_len);
    if (comp < 0)
      return true;
    if (comp > 0)
      return false;
    return (this_len < other_len);
  }
  
  bool Value::CZString::operator==(const CZString& other) const {
    if (!cstr_)
      return index_ == other.index_;
    // return strcmp(cstr_, other.cstr_) == 0;
    // Assume both are strings.
    unsigned this_len = this->storage_.length_;
    unsigned other_len = other.storage_.length_;
    if (this_len != other_len)
      return false;
    JSON_ASSERT(this->cstr_ && other.cstr_);
    int comp = memcmp(this->cstr_, other.cstr_, this_len);
    return comp == 0;
  }
  
  ArrayIndex Value::CZString::index() const { return index_; }
  
  // const char* Value::CZString::c_str() const { return cstr_; }
  const char* Value::CZString::data() const { return cstr_; }
  unsigned Value::CZString::length() const { return storage_.length_; }
  bool Value::CZString::isStaticString() const {
    return storage_.policy_ == noDuplication;
  }
  
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // class Value::Value
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  // //////////////////////////////////////////////////////////////////
  
  /*! \internal Default constructor initialization must be equivalent to:
   * memset( this, 0, sizeof(Value) )
   * This optimization is used in ValueInternalMap fast allocator.
   */
  Value::Value(ValueType type) {
    static char const emptyString[] = "";
    initBasic(type);
    switch (type) {
    case nullValue:
      break;
    case intValue:
    case uintValue:
      value_.int_ = 0;
      break;
    case realValue:
      value_.real_ = 0.0;
      break;
    case stringValue:
      // allocated_ == false, so this is safe.
      value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
      break;
    case arrayValue:
    case objectValue:
      value_.map_ = new ObjectValues();
      break;
    case booleanValue:
      value_.bool_ = false;
      break;
    default:
      JSON_ASSERT_UNREACHABLE;
    }
  }
  
  Value::Value(Int value) {
    initBasic(intValue);
    value_.int_ = value;
  }
  
  Value::Value(UInt value) {
    initBasic(uintValue);
    value_.uint_ = value;
  }
  #if defined(JSON_HAS_INT64)
  Value::Value(Int64 value) {
    initBasic(intValue);
    value_.int_ = value;
  }
  Value::Value(UInt64 value) {
    initBasic(uintValue);
    value_.uint_ = value;
  }
  #endif // defined(JSON_HAS_INT64)
  
  Value::Value(double value) {
    initBasic(realValue);
    value_.real_ = value;
  }
  
  Value::Value(const char* value) {
    initBasic(stringValue, true);
    JSON_ASSERT_MESSAGE(value != nullptr,
                        "Null Value Passed to Value Constructor");
    value_.string_ = duplicateAndPrefixStringValue(
        value, static_cast<unsigned>(strlen(value)));
  }
  
  Value::Value(const char* begin, const char* end) {
    initBasic(stringValue, true);
    value_.string_ =
        duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
  }
  
  Value::Value(const String& value) {
    initBasic(stringValue, true);
    value_.string_ = duplicateAndPrefixStringValue(
        value.data(), static_cast<unsigned>(value.length()));
  }
  
  Value::Value(const StaticString& value) {
    initBasic(stringValue);
    value_.string_ = const_cast<char*>(value.c_str());
  }
  
  #ifdef JSON_USE_CPPTL
  Value::Value(const CppTL::ConstString& value) {
    initBasic(stringValue, true);
    value_.string_ = duplicateAndPrefixStringValue(
        value, static_cast<unsigned>(value.length()));
  }
  #endif
  
  Value::Value(bool value) {
    initBasic(booleanValue);
    value_.bool_ = value;
  }
  
  Value::Value(const Value& other) {
    dupPayload(other);
    dupMeta(other);
  }
  
  Value::Value(Value&& other) {
    initBasic(nullValue);
    swap(other);
  }
  
  Value::~Value() {
    releasePayload();
    value_.uint_ = 0;
  }
  
  Value& Value::operator=(const Value& other) {
    Value(other).swap(*this);
    return *this;
  }
  
  Value& Value::operator=(Value&& other) {
    other.swap(*this);
    return *this;
  }
  
  void Value::swapPayload(Value& other) {
    std::swap(bits_, other.bits_);
    std::swap(value_, other.value_);
  }
  
  void Value::copyPayload(const Value& other) {
    releasePayload();
    dupPayload(other);
  }
  
  void Value::swap(Value& other) {
    swapPayload(other);
    std::swap(comments_, other.comments_);
    std::swap(start_, other.start_);
    std::swap(limit_, other.limit_);
  }
  
  void Value::copy(const Value& other) {
    copyPayload(other);
    dupMeta(other);
  }
  
  ValueType Value::type() const {
    return static_cast<ValueType>(bits_.value_type_);
  }
  
  int Value::compare(const Value& other) const {
    if (*this < other)
      return -1;
    if (*this > other)
      return 1;
    return 0;
  }
  
  bool Value::operator<(const Value& other) const {
    int typeDelta = type() - other.type();
    if (typeDelta)
      return typeDelta < 0 ? true : false;
    switch (type()) {
    case nullValue:
      return false;
    case intValue:
      return value_.int_ < other.value_.int_;
    case uintValue:
      return value_.uint_ < other.value_.uint_;
    case realValue:
      return value_.real_ < other.value_.real_;
    case booleanValue:
      return value_.bool_ < other.value_.bool_;
    case stringValue: {
      if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
        if (other.value_.string_)
          return true;
        else
          return false;
      }
      unsigned this_len;
      unsigned other_len;
      char const* this_str;
      char const* other_str;
      decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                           &this_str);
      decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
                           &other_str);
      unsigned min_len = std::min<unsigned>(this_len, other_len);
      JSON_ASSERT(this_str && other_str);
      int comp = memcmp(this_str, other_str, min_len);
      if (comp < 0)
        return true;
      if (comp > 0)
        return false;
      return (this_len < other_len);
    }
    case arrayValue:
    case objectValue: {
      int delta = int(value_.map_->size() - other.value_.map_->size());
      if (delta)
        return delta < 0;
      return (*value_.map_) < (*other.value_.map_);
    }
    default:
      JSON_ASSERT_UNREACHABLE;
    }
    return false; // unreachable
  }
  
  bool Value::operator<=(const Value& other) const { return !(other < *this); }
  
  bool Value::operator>=(const Value& other) const { return !(*this < other); }
  
  bool Value::operator>(const Value& other) const { return other < *this; }
  
  bool Value::operator==(const Value& other) const {
    if (type() != other.type())
      return false;
    switch (type()) {
    case nullValue:
      return true;
    case intValue:
      return value_.int_ == other.value_.int_;
    case uintValue:
      return value_.uint_ == other.value_.uint_;
    case realValue:
      return value_.real_ == other.value_.real_;
    case booleanValue:
      return value_.bool_ == other.value_.bool_;
    case stringValue: {
      if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
        return (value_.string_ == other.value_.string_);
      }
      unsigned this_len;
      unsigned other_len;
      char const* this_str;
      char const* other_str;
      decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                           &this_str);
      decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
                           &other_str);
      if (this_len != other_len)
        return false;
      JSON_ASSERT(this_str && other_str);
      int comp = memcmp(this_str, other_str, this_len);
      return comp == 0;
    }
    case arrayValue:
    case objectValue:
      return value_.map_->size() == other.value_.map_->size() &&
             (*value_.map_) == (*other.value_.map_);
    default:
      JSON_ASSERT_UNREACHABLE;
    }
    return false; // unreachable
  }
  
  bool Value::operator!=(const Value& other) const { return !(*this == other); }
  
  const char* Value::asCString() const {
    JSON_ASSERT_MESSAGE(type() == stringValue,
                        "in Json::Value::asCString(): requires stringValue");
    if (value_.string_ == nullptr)
      return nullptr;
    unsigned this_len;
    char const* this_str;
    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                         &this_str);
    return this_str;
  }
  
  #if JSONCPP_USING_SECURE_MEMORY
  unsigned Value::getCStringLength() const {
    JSON_ASSERT_MESSAGE(type() == stringValue,
                        "in Json::Value::asCString(): requires stringValue");
    if (value_.string_ == 0)
      return 0;
    unsigned this_len;
    char const* this_str;
    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                         &this_str);
    return this_len;
  }
  #endif
  
  bool Value::getString(char const** begin, char const** end) const {
    if (type() != stringValue)
      return false;
    if (value_.string_ == nullptr)
      return false;
    unsigned length;
    decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
                         begin);
    *end = *begin + length;
    return true;
  }
  
  String Value::asString() const {
    switch (type()) {
    case nullValue:
      return "";
    case stringValue: {
      if (value_.string_ == nullptr)
        return "";
      unsigned this_len;
      char const* this_str;
      decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                           &this_str);
      return String(this_str, this_len);
    }
    case booleanValue:
      return value_.bool_ ? "true" : "false";
    case intValue:
      return valueToString(value_.int_);
    case uintValue:
      return valueToString(value_.uint_);
    case realValue:
      return valueToString(value_.real_);
    default:
      JSON_FAIL_MESSAGE("Type is not convertible to string");
    }
  }
  
  #ifdef JSON_USE_CPPTL
  CppTL::ConstString Value::asConstString() const {
    unsigned len;
    char const* str;
    decodePrefixedString(isAllocated(), value_.string_, &len, &str);
    return CppTL::ConstString(str, len);
  }
  #endif
  
  Value::Int Value::asInt() const {
    switch (type()) {
    case intValue:
      JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
      return Int(value_.int_);
    case uintValue:
      JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
      return Int(value_.uint_);
    case realValue:
      JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
                          "double out of Int range");
      return Int(value_.real_);
    case nullValue:
      return 0;
    case booleanValue:
      return value_.bool_ ? 1 : 0;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to Int.");
  }
  
  Value::UInt Value::asUInt() const {
    switch (type()) {
    case intValue:
      JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
      return UInt(value_.int_);
    case uintValue:
      JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
      return UInt(value_.uint_);
    case realValue:
      JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
                          "double out of UInt range");
      return UInt(value_.real_);
    case nullValue:
      return 0;
    case booleanValue:
      return value_.bool_ ? 1 : 0;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
  }
  
  #if defined(JSON_HAS_INT64)
  
  Value::Int64 Value::asInt64() const {
    switch (type()) {
    case intValue:
      return Int64(value_.int_);
    case uintValue:
      JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
      return Int64(value_.uint_);
    case realValue:
      JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
                          "double out of Int64 range");
      return Int64(value_.real_);
    case nullValue:
      return 0;
    case booleanValue:
      return value_.bool_ ? 1 : 0;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
  }
  
  Value::UInt64 Value::asUInt64() const {
    switch (type()) {
    case intValue:
      JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
      return UInt64(value_.int_);
    case uintValue:
      return UInt64(value_.uint_);
    case realValue:
      JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
                          "double out of UInt64 range");
      return UInt64(value_.real_);
    case nullValue:
      return 0;
    case booleanValue:
      return value_.bool_ ? 1 : 0;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
  }
  #endif // if defined(JSON_HAS_INT64)
  
  LargestInt Value::asLargestInt() const {
  #if defined(JSON_NO_INT64)
    return asInt();
  #else
    return asInt64();
  #endif
  }
  
  LargestUInt Value::asLargestUInt() const {
  #if defined(JSON_NO_INT64)
    return asUInt();
  #else
    return asUInt64();
  #endif
  }
  
  double Value::asDouble() const {
    switch (type()) {
    case intValue:
      return static_cast<double>(value_.int_);
    case uintValue:
  #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
      return static_cast<double>(value_.uint_);
  #else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
      return integerToDouble(value_.uint_);
  #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
    case realValue:
      return value_.real_;
    case nullValue:
      return 0.0;
    case booleanValue:
      return value_.bool_ ? 1.0 : 0.0;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to double.");
  }
  
  float Value::asFloat() const {
    switch (type()) {
    case intValue:
      return static_cast<float>(value_.int_);
    case uintValue:
  #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
      return static_cast<float>(value_.uint_);
  #else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
      // This can fail (silently?) if the value is bigger than MAX_FLOAT.
      return static_cast<float>(integerToDouble(value_.uint_));
  #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
    case realValue:
      return static_cast<float>(value_.real_);
    case nullValue:
      return 0.0;
    case booleanValue:
      return value_.bool_ ? 1.0f : 0.0f;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to float.");
  }
  
  bool Value::asBool() const {
    switch (type()) {
    case booleanValue:
      return value_.bool_;
    case nullValue:
      return false;
    case intValue:
      return value_.int_ ? true : false;
    case uintValue:
      return value_.uint_ ? true : false;
    case realValue:
      // This is kind of strange. Not recommended.
      return (value_.real_ != 0.0) ? true : false;
    default:
      break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to bool.");
  }
  
  bool Value::isConvertibleTo(ValueType other) const {
    switch (other) {
    case nullValue:
      return (isNumeric() && asDouble() == 0.0) ||
             (type() == booleanValue && value_.bool_ == false) ||
             (type() == stringValue && asString().empty()) ||
             (type() == arrayValue && value_.map_->empty()) ||
             (type() == objectValue && value_.map_->empty()) ||
             type() == nullValue;
    case intValue:
      return isInt() ||
             (type() == realValue && InRange(value_.real_, minInt, maxInt)) ||
             type() == booleanValue || type() == nullValue;
    case uintValue:
      return isUInt() ||
             (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
             type() == booleanValue || type() == nullValue;
    case realValue:
      return isNumeric() || type() == booleanValue || type() == nullValue;
    case booleanValue:
      return isNumeric() || type() == booleanValue || type() == nullValue;
    case stringValue:
      return isNumeric() || type() == booleanValue || type() == stringValue ||
             type() == nullValue;
    case arrayValue:
      return type() == arrayValue || type() == nullValue;
    case objectValue:
      return type() == objectValue || type() == nullValue;
    }
    JSON_ASSERT_UNREACHABLE;
    return false;
  }
  
  /// Number of values in array or object
  ArrayIndex Value::size() const {
    switch (type()) {
    case nullValue:
    case intValue:
    case uintValue:
    case realValue:
    case booleanValue:
    case stringValue:
      return 0;
    case arrayValue: // size of the array is highest index + 1
      if (!value_.map_->empty()) {
        ObjectValues::const_iterator itLast = value_.map_->end();
        --itLast;
        return (*itLast).first.index() + 1;
      }
      return 0;
    case objectValue:
      return ArrayIndex(value_.map_->size());
    }
    JSON_ASSERT_UNREACHABLE;
    return 0; // unreachable;
  }
  
  bool Value::empty() const {
    if (isNull() || isArray() || isObject())
      return size() == 0u;
    else
      return false;
  }
  
  Value::operator bool() const { return !isNull(); }
  
  void Value::clear() {
    JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
                            type() == objectValue,
                        "in Json::Value::clear(): requires complex value");
    start_ = 0;
    limit_ = 0;
    switch (type()) {
    case arrayValue:
    case objectValue:
      value_.map_->clear();
      break;
    default:
      break;
    }
  }
  
  void Value::resize(ArrayIndex newSize) {
    JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                        "in Json::Value::resize(): requires arrayValue");
    if (type() == nullValue)
      *this = Value(arrayValue);
    ArrayIndex oldSize = size();
    if (newSize == 0)
      clear();
    else if (newSize > oldSize)
      this->operator[](newSize - 1);
    else {
      for (ArrayIndex index = newSize; index < oldSize; ++index) {
        value_.map_->erase(index);
      }
      JSON_ASSERT(size() == newSize);
    }
  }
  
  Value& Value::operator[](ArrayIndex index) {
    JSON_ASSERT_MESSAGE(
        type() == nullValue || type() == arrayValue,
        "in Json::Value::operator[](ArrayIndex): requires arrayValue");
    if (type() == nullValue)
      *this = Value(arrayValue);
    CZString key(index);
    auto it = value_.map_->lower_bound(key);
    if (it != value_.map_->end() && (*it).first == key)
      return (*it).second;
  
    ObjectValues::value_type defaultValue(key, nullSingleton());
    it = value_.map_->insert(it, defaultValue);
    return (*it).second;
  }
  
  Value& Value::operator[](int index) {
    JSON_ASSERT_MESSAGE(
        index >= 0,
        "in Json::Value::operator[](int index): index cannot be negative");
    return (*this)[ArrayIndex(index)];
  }
  
  const Value& Value::operator[](ArrayIndex index) const {
    JSON_ASSERT_MESSAGE(
        type() == nullValue || type() == arrayValue,
        "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
    if (type() == nullValue)
      return nullSingleton();
    CZString key(index);
    ObjectValues::const_iterator it = value_.map_->find(key);
    if (it == value_.map_->end())
      return nullSingleton();
    return (*it).second;
  }
  
  const Value& Value::operator[](int index) const {
    JSON_ASSERT_MESSAGE(
        index >= 0,
        "in Json::Value::operator[](int index) const: index cannot be negative");
    return (*this)[ArrayIndex(index)];
  }
  
  void Value::initBasic(ValueType type, bool allocated) {
    setType(type);
    setIsAllocated(allocated);
    comments_ = Comments{};
    start_ = 0;
    limit_ = 0;
  }
  
  void Value::dupPayload(const Value& other) {
    setType(other.type());
    setIsAllocated(false);
    switch (type()) {
    case nullValue:
    case intValue:
    case uintValue:
    case realValue:
    case booleanValue:
      value_ = other.value_;
      break;
    case stringValue:
      if (other.value_.string_ && other.isAllocated()) {
        unsigned len;
        char const* str;
        decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
                             &str);
        value_.string_ = duplicateAndPrefixStringValue(str, len);
        setIsAllocated(true);
      } else {
        value_.string_ = other.value_.string_;
      }
      break;
    case arrayValue:
    case objectValue:
      value_.map_ = new ObjectValues(*other.value_.map_);
      break;
    default:
      JSON_ASSERT_UNREACHABLE;
    }
  }
  
  void Value::releasePayload() {
    switch (type()) {
    case nullValue:
    case intValue:
    case uintValue:
    case realValue:
    case booleanValue:
      break;
    case stringValue:
      if (isAllocated())
        releasePrefixedStringValue(value_.string_);
      break;
    case arrayValue:
    case objectValue:
      delete value_.map_;
      break;
    default:
      JSON_ASSERT_UNREACHABLE;
    }
  }
  
  void Value::dupMeta(const Value& other) {
    comments_ = other.comments_;
    start_ = other.start_;
    limit_ = other.limit_;
  }
  
  // Access an object value by name, create a null member if it does not exist.
  // @pre Type of '*this' is object or null.
  // @param key is null-terminated.
  Value& Value::resolveReference(const char* key) {
    JSON_ASSERT_MESSAGE(
        type() == nullValue || type() == objectValue,
        "in Json::Value::resolveReference(): requires objectValue");
    if (type() == nullValue)
      *this = Value(objectValue);
    CZString actualKey(key, static_cast<unsigned>(strlen(key)),
                       CZString::noDuplication); // NOTE!
    auto it = value_.map_->lower_bound(actualKey);
    if (it != value_.map_->end() && (*it).first == actualKey)
      return (*it).second;
  
    ObjectValues::value_type defaultValue(actualKey, nullSingleton());
    it = value_.map_->insert(it, defaultValue);
    Value& value = (*it).second;
    return value;
  }
  
  // @param key is not null-terminated.
  Value& Value::resolveReference(char const* key, char const* end) {
    JSON_ASSERT_MESSAGE(
        type() == nullValue || type() == objectValue,
        "in Json::Value::resolveReference(key, end): requires objectValue");
    if (type() == nullValue)
      *this = Value(objectValue);
    CZString actualKey(key, static_cast<unsigned>(end - key),
                       CZString::duplicateOnCopy);
    auto it = value_.map_->lower_bound(actualKey);
    if (it != value_.map_->end() && (*it).first == actualKey)
      return (*it).second;
  
    ObjectValues::value_type defaultValue(actualKey, nullSingleton());
    it = value_.map_->insert(it, defaultValue);
    Value& value = (*it).second;
    return value;
  }
  
  Value Value::get(ArrayIndex index, const Value& defaultValue) const {
    const Value* value = &((*this)[index]);
    return value == &nullSingleton() ? defaultValue : *value;
  }
  
  bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
  
  Value const* Value::find(char const* begin, char const* end) const {
    JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                        std::string("in Json::Value::find(begin, end): requires "
                        "objectValue or nullValue: got ")
                        .append(asCString()).append(": ")
                        .append(std::to_string(type())));
    if (type() == nullValue)
      return nullptr;
    CZString actualKey(begin, static_cast<unsigned>(end - begin),
                       CZString::noDuplication);
    ObjectValues::const_iterator it = value_.map_->find(actualKey);
    if (it == value_.map_->end())
      return nullptr;
    return &(*it).second;
  }
  Value* Value::demand(char const* begin, char const* end) {
    JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                        "in Json::Value::demand(begin, end): requires "
                        "objectValue or nullValue");
    return &resolveReference(begin, end);
  }
  const Value& Value::operator[](const char* key) const {
    Value const* found = find(key, key + strlen(key));
    if (!found)
      return nullSingleton();
    return *found;
  }
  Value const& Value::operator[](const String& key) const {
    Value const* found = find(key.data(), key.data() + key.length());
    if (!found)
      return nullSingleton();
    return *found;
  }
  
  Value& Value::operator[](const char* key) {
    return resolveReference(key, key + strlen(key));
  }
  
  Value& Value::operator[](const String& key) {
    return resolveReference(key.data(), key.data() + key.length());
  }
  
  Value& Value::operator[](const StaticString& key) {
    return resolveReference(key.c_str());
  }
  
  #ifdef JSON_USE_CPPTL
  Value& Value::operator[](const CppTL::ConstString& key) {
    return resolveReference(key.c_str(), key.end_c_str());
  }
  Value const& Value::operator[](CppTL::ConstString const& key) const {
    Value const* found = find(key.c_str(), key.end_c_str());
    if (!found)
      return nullSingleton();
    return *found;
  }
  #endif
  
  Value& Value::append(const Value& value) { return (*this)[size()] = value; }
  
  Value& Value::append(Value&& value) {
    return (*this)[size()] = std::move(value);
  }
  
  Value Value::get(char const* begin,
                   char const* end,
                   Value const& defaultValue) const {
    Value const* found = find(begin, end);
    return !found ? defaultValue : *found;
  }
  Value Value::get(char const* key, Value const& defaultValue) const {
    return get(key, key + strlen(key), defaultValue);
  }
  Value Value::get(String const& key, Value const& defaultValue) const {
    return get(key.data(), key.data() + key.length(), defaultValue);
  }
  
  bool Value::removeMember(const char* begin, const char* end, Value* removed) {
    if (type() != objectValue) {
      return false;
    }
    CZString actualKey(begin, static_cast<unsigned>(end - begin),
                       CZString::noDuplication);
    auto it = value_.map_->find(actualKey);
    if (it == value_.map_->end())
      return false;
    if (removed)
      *removed = std::move(it->second);
    value_.map_->erase(it);
    return true;
  }
  bool Value::removeMember(const char* key, Value* removed) {
    return removeMember(key, key + strlen(key), removed);
  }
  bool Value::removeMember(String const& key, Value* removed) {
    return removeMember(key.data(), key.data() + key.length(), removed);
  }
  void Value::removeMember(const char* key) {
    JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                        "in Json::Value::removeMember(): requires objectValue");
    if (type() == nullValue)
      return;
  
    CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
    value_.map_->erase(actualKey);
  }
  void Value::removeMember(const String& key) { removeMember(key.c_str()); }
  
  bool Value::removeIndex(ArrayIndex index, Value* removed) {
    if (type() != arrayValue) {
      return false;
    }
    CZString key(index);
    auto it = value_.map_->find(key);
    if (it == value_.map_->end()) {
      return false;
    }
    if (removed)
      *removed = it->second;
    ArrayIndex oldSize = size();
    // shift left all items left, into the place of the "removed"
    for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
      CZString keey(i);
      (*value_.map_)[keey] = (*this)[i + 1];
    }
    // erase the last one ("leftover")
    CZString keyLast(oldSize - 1);
    auto itLast = value_.map_->find(keyLast);
    value_.map_->erase(itLast);
    return true;
  }
  
  #ifdef JSON_USE_CPPTL
  Value Value::get(const CppTL::ConstString& key,
                   const Value& defaultValue) const {
    return get(key.c_str(), key.end_c_str(), defaultValue);
  }
  #endif
  
  bool Value::isMember(char const* begin, char const* end) const {
    Value const* value = find(begin, end);
    return nullptr != value;
  }
  bool Value::isMember(char const* key) const {
    return isMember(key, key + strlen(key));
  }
  bool Value::isMember(String const& key) const {
    return isMember(key.data(), key.data() + key.length());
  }
  
  #ifdef JSON_USE_CPPTL
  bool Value::isMember(const CppTL::ConstString& key) const {
    return isMember(key.c_str(), key.end_c_str());
  }
  #endif
  
  Value::Members Value::getMemberNames() const {
    JSON_ASSERT_MESSAGE(
        type() == nullValue || type() == objectValue,
        "in Json::Value::getMemberNames(), value must be objectValue");
    if (type() == nullValue)
      return Value::Members();
    Members members;
    members.reserve(value_.map_->size());
    ObjectValues::const_iterator it = value_.map_->begin();
    ObjectValues::const_iterator itEnd = value_.map_->end();
    for (; it != itEnd; ++it) {
      members.push_back(String((*it).first.data(), (*it).first.length()));
    }
    return members;
  }
  //
  //# ifdef JSON_USE_CPPTL
  // EnumMemberNames
  // Value::enumMemberNames() const
  //{
  //   if ( type() == objectValue )
  //   {
  //      return CppTL::Enum::any(  CppTL::Enum::transform(
  //         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
  //         MemberNamesTransform() ) );
  //   }
  //   return EnumMemberNames();
  //}
  //
  //
  // EnumValues
  // Value::enumValues() const
  //{
  //   if ( type() == objectValue  ||  type() == arrayValue )
  //      return CppTL::Enum::anyValues( *(value_.map_),
  //                                     CppTL::Type<const Value &>() );
  //   return EnumValues();
  //}
  //
  //# endif
  
  static bool IsIntegral(double d) {
    double integral_part;
    return modf(d, &integral_part) == 0.0;
  }
  
  bool Value::isNull() const { return type() == nullValue; }
  
  bool Value::isBool() const { return type() == booleanValue; }
  
  bool Value::isInt() const {
    switch (type()) {
    case intValue:
  #if defined(JSON_HAS_INT64)
      return value_.int_ >= minInt && value_.int_ <= maxInt;
  #else
      return true;
  #endif
    case uintValue:
      return value_.uint_ <= UInt(maxInt);
    case realValue:
      return value_.real_ >= minInt && value_.real_ <= maxInt &&
             IsIntegral(value_.real_);
    default:
      break;
    }
    return false;
  }
  
  bool Value::isUInt() const {
    switch (type()) {
    case intValue:
  #if defined(JSON_HAS_INT64)
      return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
  #else
      return value_.int_ >= 0;
  #endif
    case uintValue:
  #if defined(JSON_HAS_INT64)
      return value_.uint_ <= maxUInt;
  #else
      return true;
  #endif
    case realValue:
      return value_.real_ >= 0 && value_.real_ <= maxUInt &&
             IsIntegral(value_.real_);
    default:
      break;
    }
    return false;
  }
  
  bool Value::isInt64() const {
  #if defined(JSON_HAS_INT64)
    switch (type()) {
    case intValue:
      return true;
    case uintValue:
      return value_.uint_ <= UInt64(maxInt64);
    case realValue:
      // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
      // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
      // require the value to be strictly less than the limit.
      return value_.real_ >= double(minInt64) &&
             value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
    default:
      break;
    }
  #endif // JSON_HAS_INT64
    return false;
  }
  
  bool Value::isUInt64() const {
  #if defined(JSON_HAS_INT64)
    switch (type()) {
    case intValue:
      return value_.int_ >= 0;
    case uintValue:
      return true;
    case realValue:
      // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
      // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
      // require the value to be strictly less than the limit.
      return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
             IsIntegral(value_.real_);
    default:
      break;
    }
  #endif // JSON_HAS_INT64
    return false;
  }
  
  bool Value::isIntegral() const {
    switch (type()) {
    case intValue:
    case uintValue:
      return true;
    case realValue:
  #if defined(JSON_HAS_INT64)
      // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
      // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
      // require the value to be strictly less than the limit.
      return value_.real_ >= double(minInt64) &&
             value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
  #else
      return value_.real_ >= minInt && value_.real_ <= maxUInt &&
             IsIntegral(value_.real_);
  #endif // JSON_HAS_INT64
    default:
      break;
    }
    return false;
  }
  
  bool Value::isDouble() const {
    return type() == intValue || type() == uintValue || type() == realValue;
  }
  
  bool Value::isNumeric() const { return isDouble(); }
  
  bool Value::isString() const { return type() == stringValue; }
  
  bool Value::isArray() const { return type() == arrayValue; }
  
  bool Value::isObject() const { return type() == objectValue; }
  
  Value::Comments::Comments(const Comments& that)
      : ptr_{cloneUnique(that.ptr_)} {}
  
  Value::Comments::Comments(Comments&& that)
      : ptr_{std::move(that.ptr_)} {}
  
  Value::Comments& Value::Comments::operator=(const Comments& that) {
    ptr_ = cloneUnique(that.ptr_);
    return *this;
  }
  
  Value::Comments& Value::Comments::operator=(Comments&& that) {
    ptr_ = std::move(that.ptr_);
    return *this;
  }
  
  bool Value::Comments::has(CommentPlacement slot) const {
    return ptr_ && !(*ptr_)[slot].empty();
  }
  
  String Value::Comments::get(CommentPlacement slot) const {
    if (!ptr_)
      return {};
    return (*ptr_)[slot];
  }
  
  void Value::Comments::set(CommentPlacement slot, String comment) {
    if (!ptr_) {
      ptr_ = std::unique_ptr<Array>(new Array());
    }
    (*ptr_)[slot] = std::move(comment);
  }
  
  void Value::setComment(String comment, CommentPlacement placement) {
    if (!comment.empty() && (comment.back() == '\n')) {
      // Always discard trailing newline, to aid indentation.
      comment.pop_back();
    }
    JSON_ASSERT(!comment.empty());
    JSON_ASSERT_MESSAGE(
        comment[0] == '\0' || comment[0] == '/',
        "in Json::Value::setComment(): Comments must start with /");
    comments_.set(placement, std::move(comment));
  }
  
  bool Value::hasComment(CommentPlacement placement) const {
    return comments_.has(placement);
  }
  
  String Value::getComment(CommentPlacement placement) const {
    return comments_.get(placement);
  }
  
  void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
  
  void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }
  
  ptrdiff_t Value::getOffsetStart() const { return start_; }
  
  ptrdiff_t Value::getOffsetLimit() const { return limit_; }
  
  String Value::toStyledString() const {
    StreamWriterBuilder builder;
  
    String out = this->hasComment(commentBefore) ? "\n" : "";
    out += Json::writeString(builder, *this);
    out += '\n';
  
    return out;
  }
  
  Value::const_iterator Value::begin() const {
    switch (type()) {
    case arrayValue:
    case objectValue:
      if (value_.map_)
        return const_iterator(value_.map_->begin());
      break;
    default:
      break;
    }
    return {};
  }
  
  Value::const_iterator Value::end() const {
    switch (type()) {
    case arrayValue:
    case objectValue:
      if (value_.map_)
        return const_iterator(value_.map_->end());
      break;
    default:
      break;
    }
    return {};
  }
  
  Value::iterator Value::begin() {
    switch (type()) {
    case arrayValue:
    case objectValue:
      if (value_.map_)
        return iterator(value_.map_->begin());
      break;
    default:
      break;
    }
    return iterator();
  }
  
  Value::iterator Value::end() {
    switch (type()) {
    case arrayValue:
    case objectValue:
      if (value_.map_)
        return iterator(value_.map_->end());
      break;
    default:
      break;
    }
    return iterator();
  }
  
  // class PathArgument
  // //////////////////////////////////////////////////////////////////
  
  PathArgument::PathArgument() : key_() {}
  
  PathArgument::PathArgument(ArrayIndex index)
      : key_(), index_(index), kind_(kindIndex) {}
  
  PathArgument::PathArgument(const char* key)
      : key_(key), index_(), kind_(kindKey) {}
  
  PathArgument::PathArgument(const String& key)
      : key_(key.c_str()), index_(), kind_(kindKey) {}
  
  // class Path
  // //////////////////////////////////////////////////////////////////
  
  Path::Path(const String& path,
             const PathArgument& a1,
             const PathArgument& a2,
             const PathArgument& a3,
             const PathArgument& a4,
             const PathArgument& a5) {
    InArgs in;
    in.reserve(5);
    in.push_back(&a1);
    in.push_back(&a2);
    in.push_back(&a3);
    in.push_back(&a4);
    in.push_back(&a5);
    makePath(path, in);
  }
  
  void Path::makePath(const String& path, const InArgs& in) {
    const char* current = path.c_str();
    const char* end = current + path.length();
    auto itInArg = in.begin();
    while (current != end) {
      if (*current == '[') {
        ++current;
        if (*current == '%')
          addPathInArg(path, in, itInArg, PathArgument::kindIndex);
        else {
          ArrayIndex index = 0;
          for (; current != end && *current >= '0' && *current <= '9'; ++current)
            index = index * 10 + ArrayIndex(*current - '0');
          args_.push_back(index);
        }
        if (current == end || *++current != ']')
          invalidPath(path, int(current - path.c_str()));
      } else if (*current == '%') {
        addPathInArg(path, in, itInArg, PathArgument::kindKey);
        ++current;
      } else if (*current == '.' || *current == ']') {
        ++current;
      } else {
        const char* beginName = current;
        while (current != end && !strchr("[.", *current))
          ++current;
        args_.push_back(String(beginName, current));
      }
    }
  }
  
  void Path::addPathInArg(const String& /*path*/,
                          const InArgs& in,
                          InArgs::const_iterator& itInArg,
                          PathArgument::Kind kind) {
    if (itInArg == in.end()) {
      // Error: missing argument %d
    } else if ((*itInArg)->kind_ != kind) {
      // Error: bad argument type
    } else {
      args_.push_back(**itInArg++);
    }
  }
  
  void Path::invalidPath(const String& /*path*/, int /*location*/) {
    // Error: invalid path.
  }
  
  const Value& Path::resolve(const Value& root) const {
    const Value* node = &root;
    for (const auto& arg : args_) {
      if (arg.kind_ == PathArgument::kindIndex) {
        if (!node->isArray() || !node->isValidIndex(arg.index_)) {
          // Error: unable to resolve path (array value expected at position...
          return Value::null;
        }
        node = &((*node)[arg.index_]);
      } else if (arg.kind_ == PathArgument::kindKey) {
        if (!node->isObject()) {
          // Error: unable to resolve path (object value expected at position...)
          return Value::null;
        }
        node = &((*node)[arg.key_]);
        if (node == &Value::nullSingleton()) {
          // Error: unable to resolve path (object has no member named '' at
          // position...)
          return Value::null;
        }
      }
    }
    return *node;
  }
  
  Value Path::resolve(const Value& root, const Value& defaultValue) const {
    const Value* node = &root;
    for (const auto& arg : args_) {
      if (arg.kind_ == PathArgument::kindIndex) {
        if (!node->isArray() || !node->isValidIndex(arg.index_))
          return defaultValue;
        node = &((*node)[arg.index_]);
      } else if (arg.kind_ == PathArgument::kindKey) {
        if (!node->isObject())
          return defaultValue;
        node = &((*node)[arg.key_]);
        if (node == &Value::nullSingleton())
          return defaultValue;
      }
    }
    return *node;
  }
  
  Value& Path::make(Value& root) const {
    Value* node = &root;
    for (const auto& arg : args_) {
      if (arg.kind_ == PathArgument::kindIndex) {
        if (!node->isArray()) {
          // Error: node is not an array at position ...
        }
        node = &((*node)[arg.index_]);
      } else if (arg.kind_ == PathArgument::kindKey) {
        if (!node->isObject()) {
          // Error: node is not an object at position...
        }
        node = &((*node)[arg.key_]);
      }
    }
    return *node;
  }
  
  } // namespace Json
  