// 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

#include "assertions.h"
#include "value.h"
#include "writer.h"

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <iostream>
#include <sstream>
#include <utility>

#ifdef JSONCPP_HAS_STRING_VIEW
#	include <string_view>
#endif

// 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
	Value const& Value::nullSingleton() {
		static Value const nullStatic;
		return nullStatic;
	}

#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) &&
			!(static_cast<U>(d) == min && d != static_cast<double>(min));
	}
#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) &&
			!(static_cast<U>(d) == min && d != integerToDouble(min));
	}
#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;

		auto 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");
		size_t actualLength = sizeof(length) + length + 1;
		auto 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_USE_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_USE_SECURE_MEMORY
	static inline void releasePrefixedStringValue(char* value) {
		free(value);
	}
	static inline void releaseStringValue(char* value, unsigned) {
		free(value);
	}
#endif // JSONCPP_USE_SECURE_MEMORY

} // namespace Json

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// ValueInternals...
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////

#include "json_valueiterator.h"

namespace Json {

#if JSON_USE_EXCEPTION
	Exception::Exception(StringContainer msg) : msg_(std::move(msg)) {
	}
	Exception::~Exception() noexcept = default;
	char const* Exception::what() const noexcept {
		return msg_.c_str();
	}
	RuntimeError::RuntimeError(StringContainer const& msg) : Exception(msg) {
	}
	LogicError::LogicError(StringContainer const& msg) : Exception(msg) {
	}
	JSONCPP_NORETURN void throwRuntimeError(StringContainer const& msg) {
		throw RuntimeError(msg);
	}
	JSONCPP_NORETURN void throwLogicError(StringContainer const& msg) {
		throw LogicError(msg);
	}
#else // !JSON_USE_EXCEPTION
	JSONCPP_NORETURN void throwRuntimeError(StringContainer const& msg) {
		std::cerr << msg << std::endl;
		abort();
	}
	JSONCPP_NORETURN void throwLogicError(StringContainer const& msg) {
		std::cerr << msg << std::endl;
		abort();
	}
#endif

	// //////////////////////////////////////////////////////////////////
	// //////////////////////////////////////////////////////////////////
	// //////////////////////////////////////////////////////////////////
	// 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) noexcept
		: 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) noexcept {
		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 StringContainer& value) {
		initBasic(stringValue, true);
		value_.string_ = duplicateAndPrefixStringValue(
			value.data(), static_cast<unsigned>(value.length()));
	}

#ifdef JSONCPP_HAS_STRING_VIEW
	Value::Value(std::string_view value) {
		initBasic(stringValue, true);
		value_.string_ = duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));
	}
#endif

	Value::Value(const StaticString& value) {
		initBasic(stringValue);
		value_.string_ = const_cast<char*>(value.c_str());
	}

	Value::Value(bool value) {
		initBasic(booleanValue);
		value_.bool_ = value;
	}

	Value::Value(const Value& other) {
		dupPayload(other);
		dupMeta(other);
	}

	Value::Value(Value&& other) noexcept {
		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) noexcept {
		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;
		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)) {
					return other.value_.string_ != nullptr;
				}
				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: {
				auto thisSize = value_.map_->size();
				auto otherSize = other.value_.map_->size();
				if (thisSize != otherSize)
					return thisSize < otherSize;
				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_USE_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;
	}

#ifdef JSONCPP_HAS_STRING_VIEW
	ErrorCode Value::get(std::string_view& view) const {
		if (type() != stringValue)
			return INCORRECT_TYPE;
		if (value_.string_ == nullptr)
			return UNINITIALIZED;
		const char* begin;
		unsigned length;
		decodePrefixedString(this->isAllocated(), this->value_.string_, &length,  &begin);
		view = std::string_view(begin, length);
		return SUCCESS;
	}
#endif

	StringContainer 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 StringContainer(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");
		}
	}

	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_, 0u, 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)

	ErrorCode Value::get(Value::Int64& value) const {
		switch (type()) {
			case intValue:
				value = Int64(value_.int_);
				return SUCCESS;
			case uintValue:
				if (!isInt64()) {
					return BIGINT_ERROR;
				}
				value = Int64(value_.uint_);
				return SUCCESS;
			case realValue:
				// If the double value is in proximity to minInt64, it will be rounded to
				// minInt64. The correct value in this scenario is indeterminable
				if (value_.real_ == minInt64 || !InRange(value_.real_, minInt64, maxInt64)) {
					return BIGINT_ERROR;
				}
				value = Int64(value_.real_);
				return SUCCESS;
			//case nullValue:
			//	value = 0;
			//	return SUCCESS;
			case booleanValue:
				value = value_.bool_ ? 1 : 0;
				return SUCCESS;
			default:
				return INCORRECT_TYPE;
		}
	}

	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:
				// If the double value is in proximity to minInt64, it will be rounded to
				// minInt64. The correct value in this scenario is indeterminable
				JSON_ASSERT_MESSAGE(
					value_.real_ != minInt64,
					"Double value is minInt64, precise value cannot be determined");
				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_, 0u, 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
	}

	ErrorCode Value::get(double& value) const {
		switch (type()) {
			case intValue:
				value = static_cast<double>(value_.int_);
				return SUCCESS;
			case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
				value = static_cast<double>(value_.uint_);
#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
				value = integerToDouble(value_.uint_);
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
				return SUCCESS;
			case realValue:
				value = value_.real_;
				return SUCCESS;
			default:
				return INCORRECT_TYPE;
		}
	}

	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.");
	}

	ErrorCode Value::get(bool& value) const {
		switch (type()) {
			case booleanValue:
				value = value_.bool_;
				return SUCCESS;
			//case nullValue:
			//	value = false;
			//	return SUCCESS;
			case intValue:
				value = value_.int_ != 0;
				return SUCCESS;
			case uintValue:
				value = value_.uint_ != 0;
				return SUCCESS;
			case realValue: {
				// According to JavaScript language zero or NaN is regarded as false
				const auto value_classification = std::fpclassify(value_.real_);
				value = value_classification != FP_ZERO && value_classification != FP_NAN;
				return SUCCESS;
			}
			default:
				return INCORRECT_TYPE;
		}
	}

	bool Value::asBool() const {
		switch (type()) {
			case booleanValue:
				return value_.bool_;
			case nullValue:
				return false;
			case intValue:
				return value_.int_ != 0;
			case uintValue:
				return value_.uint_ != 0;
			case realValue: {
				// According to JavaScript language zero or NaN is regarded as false
				const auto value_classification = std::fpclassify(value_.real_);
				return value_classification != FP_ZERO && value_classification != FP_NAN;
			}
			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_) ||
					(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_, 0u, 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;
		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)
			for (ArrayIndex i = oldSize; i < newSize; ++i)
				(*this)[i];
		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,
							"in Json::Value::find(begin, end): requires objectValue or nullValue");
		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 const* Value::find(const StringContainer& key) const {
		return find(key.data(), key.data() + key.length());
	}

	Value const* Value::findNull(const StringContainer& key) const {
		return findValue<Value, &Value::isNull>(key);
	}
	Value const* Value::findBool(const StringContainer& key) const {
		return findValue<Value, &Value::isBool>(key);
	}
	Value const* Value::findInt(const StringContainer& key) const {
		return findValue<Value, &Value::isInt>(key);
	}
	Value const* Value::findInt64(const StringContainer& key) const {
		return findValue<Value, &Value::isInt64>(key);
	}
	Value const* Value::findUInt(const StringContainer& key) const {
		return findValue<Value, &Value::isUInt>(key);
	}
	Value const* Value::findUInt64(const StringContainer& key) const {
		return findValue<Value, &Value::isUInt64>(key);
	}
	Value const* Value::findIntegral(const StringContainer& key) const {
		return findValue<Value, &Value::isIntegral>(key);
	}
	Value const* Value::findDouble(const StringContainer& key) const {
		return findValue<Value, &Value::isDouble>(key);
	}
	Value const* Value::findNumeric(const StringContainer& key) const {
		return findValue<Value, &Value::isNumeric>(key);
	}
	Value const* Value::findString(const StringContainer& key) const {
		return findValue<Value, &Value::isString>(key);
	}
	Value const* Value::findArray(const StringContainer& key) const {
		return findValue<Value, &Value::isArray>(key);
	}
	Value const* Value::findObject(const StringContainer& key) const {
		return findValue<Value, &Value::isObject>(key);
	}

	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);
	}
#ifdef JSONCPP_HAS_STRING_VIEW
	const Value& Value::operator[](std::string_view key) const {
		Value const* found = find(key.data(), key.data() + key.length());
		if (!found)
			return nullSingleton();
		return *found;
	}
	Value& Value::operator[](std::string_view key) {
		return resolveReference(key.data(), key.data() + key.length());
	}
#else
	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 StringContainer& key) const {
		Value const* found = find(key);
		if (!found)
			return nullSingleton();
		return *found;
	}

	Value& Value::operator[](const char* key) {
		return resolveReference(key, key + strlen(key));
	}

	Value& Value::operator[](const StringContainer& key) {
		return resolveReference(key.data(), key.data() + key.length());
	}
#endif

	Value& Value::operator[](const StaticString& key) {
		return resolveReference(key.c_str());
	}

	Value& Value::append(const Value& value) {
		return append(Value(value));
	}

	Value& Value::append(Value&& value) {
		JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, "in Json::Value::append: requires arrayValue");
		if (type() == nullValue) {
			*this = Value(arrayValue);
		}
		return this->value_.map_->emplace(size(), std::move(value)).first->second;
	}

	bool Value::insert(ArrayIndex index, const Value& newValue) {
		return insert(index, Value(newValue));
	}

	bool Value::insert(ArrayIndex index, Value&& newValue) {
		JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, "in Json::Value::insert: requires arrayValue");
		ArrayIndex length = size();
		if (index > length) {
			return false;
		}
		for (ArrayIndex i = length; i > index; i--) {
			(*this)[i] = std::move((*this)[i - 1]);
		}
		(*this)[index] = std::move(newValue);
		return true;
	}

	Value Value::get(char const* begin, char const* end,
					 Value const& defaultValue) const {
		Value const* found = find(begin, end);
		return !found ? defaultValue : *found;
	}
#ifdef JSONCPP_HAS_STRING_VIEW
	Value Value::get(std::string_view key, const Value& defaultValue) const {
		return get(key.data(), key.data() + key.length(), defaultValue);
	}
#else
	Value Value::get(char const* key, Value const& defaultValue) const {
		return get(key, key + strlen(key), defaultValue);
	}
	Value Value::get(StringContainer const& key, Value const& defaultValue) const {
		return get(key.data(), key.data() + key.length(), defaultValue);
	}
#endif

	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;
	}
#ifdef JSONCPP_HAS_STRING_VIEW
	bool Value::removeMember(std::string_view key, Value* removed) {
		return removeMember(key.data(), key.data() + key.length(), removed);
	}
#else
	bool Value::removeMember(const char* key, Value* removed) {
		return removeMember(key, key + strlen(key), removed);
	}
	bool Value::removeMember(StringContainer const& key, Value* removed) {
		return removeMember(key.data(), key.data() + key.length(), removed);
	}
#endif

#ifdef JSONCPP_HAS_STRING_VIEW
	void Value::removeMember(std::string_view key) {
		JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, "in Json::Value::removeMember(): requires objectValue");
		if (type() == nullValue)
			return;

		CZString actualKey(key.data(), unsigned(key.length()), CZString::noDuplication);
		value_.map_->erase(actualKey);
	}
#else
	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 StringContainer& key) {
		removeMember(key.c_str());
	}
#endif

	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 = std::move(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;
	}

	bool Value::isMember(char const* begin, char const* end) const {
		Value const* value = find(begin, end);
		return nullptr != value;
	}
#ifdef JSONCPP_HAS_STRING_VIEW
	bool Value::isMember(std::string_view key) const {
		return isMember(key.data(), key.data() + key.length());
	}
#else
	bool Value::isMember(char const* key) const {
		return isMember(key, key + strlen(key));
	}
	bool Value::isMember(StringContainer const& key) const {
		return isMember(key.data(), key.data() + key.length());
	}
#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(StringContainer((*it).first.data(), (*it).first.length()));
		}
		return members;
	}

	size_t Value::getMemberCount() const {
		JSON_ASSERT_MESSAGE(
			type() == nullValue || type() == objectValue,
			"in Json::Value::getMemberNames(), value must be objectValue");

		return (type() == nullValue ? 0 : value_.map_->size());
	}

	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.
				// minInt64 is -2^63 which can be represented as a double, but since double
				// values in its proximity are also rounded to -2^63, we require the value
				// to be strictly greater than the limit to avoid returning 'true' for
				// values that are not in the range
				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.
				// minInt64 is -2^63 which can be represented as a double, but since double
				// values in its proximity are also rounded to -2^63, we require the value
				// to be strictly greater than the limit to avoid returning 'true' for
				// values that are not in the range
				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) noexcept
		: 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) noexcept {
		ptr_ = std::move(that.ptr_);
		return *this;
	}

	bool Value::Comments::has(CommentPlacement slot) const {
		return ptr_ && !(*ptr_)[slot].empty();
	}

	bool Value::Comments::hasAny() const {
		if (ptr_) {
			for (std::size_t slot = 0; slot < CommentPlacement::numberOfCommentPlacement; slot++) {
				if (!(*ptr_)[slot].empty())
					return true;
			}
		}
		return false;
	}

	StringContainer Value::Comments::get(CommentPlacement slot) const {
		if (!ptr_)
			return {};
		return (*ptr_)[slot];
	}

	void Value::Comments::set(CommentPlacement slot, StringContainer comment) {
		if (slot >= CommentPlacement::numberOfCommentPlacement)
			return;
		if (!ptr_)
			ptr_ = std::unique_ptr<Array>(new Array());
		(*ptr_)[slot] = std::move(comment);
	}

	void Value::setComment(StringContainer comment, CommentPlacement placement) {
		if (!comment.empty() && (comment.back() == '\n')) {
			// Always discard trailing newline, to aid indentation.
			//comment.pop_back();
		}
		// TODO
		/*JSON_ASSERT_MESSAGE(
			comment.empty() || comment[0] == '/',
			"in Json::Value::setComment(): Comments must start with /");*/
		comments_.set(placement, std::move(comment));
	}

	bool Value::hasComment() const {
		return comments_.hasAny();
	}

	bool Value::hasComment(CommentPlacement placement) const {
		return comments_.has(placement);
	}

	StringContainer 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_;
	}

	StringContainer Value::toStyledString() const {
		StreamWriterBuilder builder;

		StringContainer 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() = default;

	PathArgument::PathArgument(ArrayIndex index)
		: index_(index), kind_(kindIndex) {
	}

	PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {
	}

	PathArgument::PathArgument(StringContainer key) : key_(std::move(key)), kind_(kindKey) {
	}

	// class Path
	// //////////////////////////////////////////////////////////////////

	Path::Path(const StringContainer& 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 StringContainer& 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(StringContainer(beginName, current));
			}
		}
	}

	void Path::addPathInArg(const StringContainer& /*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 StringContainer& /*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::nullSingleton();
				}
				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::nullSingleton();
				}
				node = &((*node)[arg.key_]);
				if (node == &Value::nullSingleton()) {
					// Error: unable to resolve path (object has no member named '' at
					// position...)
					return Value::nullSingleton();
				}
			}
		}
		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
