
CPPTL support is no longer relevant to JsonCpp, and can be removed from the library. This patch removes all mentions of CPPTL, by removing all definitions and code sections conditionally compiled only when JsonCpp is used with CPPTL. Include guards are also renamed to not refer to CPPTL where appropriate.
1625 lines
47 KiB
C++
1625 lines
47 KiB
C++
// 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 <algorithm>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
// 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 JSON_USE_NULLREF
|
|
// for backwards compatibility, we'll leave these global references around, but
|
|
// DO NOT use them in JSONCPP library code any more!
|
|
// static
|
|
Value const& Value::null = Value::nullSingleton();
|
|
|
|
// static
|
|
Value const& Value::nullRef = Value::nullSingleton();
|
|
#endif
|
|
|
|
#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);
|
|
}
|
|
#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 {
|
|
|
|
#if JSON_USE_EXCEPTION
|
|
Exception::Exception(String msg) : msg_(std::move(msg)) {}
|
|
Exception::~Exception() JSONCPP_NOEXCEPT = default;
|
|
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);
|
|
}
|
|
#else // !JSON_USE_EXCEPTION
|
|
JSONCPP_NORETURN void throwRuntimeError(String const& msg) { abort(); }
|
|
JSONCPP_NORETURN void throwLogicError(String const& msg) { 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)
|
|
: 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());
|
|
}
|
|
|
|
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;
|
|
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: {
|
|
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");
|
|
}
|
|
}
|
|
|
|
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_ != 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_, 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,
|
|
"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* 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());
|
|
}
|
|
|
|
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, Value newValue) {
|
|
JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
|
|
"in Json::Value::insert: requires arrayValue");
|
|
ArrayIndex length = size();
|
|
if (index > length) {
|
|
return false;
|
|
} else {
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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());
|
|
}
|
|
// check comments array boundry.
|
|
if (slot < CommentPlacement::numberOfCommentPlacement) {
|
|
(*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() = default;
|
|
|
|
PathArgument::PathArgument(ArrayIndex index)
|
|
: index_(index), kind_(kindIndex) {}
|
|
|
|
PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {}
|
|
|
|
PathArgument::PathArgument(String key) : key_(std::move(key)), 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::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
|