| // 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 <iostream> |
| #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; |
| |
| 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_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() noexcept = default; |
| char const* Exception::what() const 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) { |
| std::cerr << msg << std::endl; |
| abort(); |
| } |
| JSONCPP_NORETURN void throwLogicError(String 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 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) 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_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; |
| 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* 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, 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; |
| } |
| 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) 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(); |
| } |
| |
| String Value::Comments::get(CommentPlacement slot) const { |
| if (!ptr_) |
| return {}; |
| return (*ptr_)[slot]; |
| } |
| |
| void Value::Comments::set(CommentPlacement slot, String comment) { |
| if (slot >= CommentPlacement::numberOfCommentPlacement) |
| return; |
| if (!ptr_) |
| ptr_ = std::unique_ptr<Array>(new Array()); |
| (*ptr_)[slot] = std::move(comment); |
| } |
| |
| void Value::setComment(String comment, CommentPlacement placement) { |
| if (!comment.empty() && (comment.back() == '\n')) { |
| // Always discard trailing newline, to aid indentation. |
| comment.pop_back(); |
| } |
| JSON_ASSERT(!comment.empty()); |
| JSON_ASSERT_MESSAGE( |
| comment[0] == '\0' || comment[0] == '/', |
| "in Json::Value::setComment(): Comments must start with /"); |
| comments_.set(placement, std::move(comment)); |
| } |
| |
| bool Value::hasComment(CommentPlacement placement) const { |
| return comments_.has(placement); |
| } |
| |
| String Value::getComment(CommentPlacement placement) const { |
| return comments_.get(placement); |
| } |
| |
| void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } |
| |
| void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } |
| |
| ptrdiff_t Value::getOffsetStart() const { return start_; } |
| |
| ptrdiff_t Value::getOffsetLimit() const { return limit_; } |
| |
| String Value::toStyledString() const { |
| StreamWriterBuilder builder; |
| |
| String out = this->hasComment(commentBefore) ? "\n" : ""; |
| out += Json::writeString(builder, *this); |
| out += '\n'; |
| |
| return out; |
| } |
| |
| Value::const_iterator Value::begin() const { |
| switch (type()) { |
| case arrayValue: |
| case objectValue: |
| if (value_.map_) |
| return const_iterator(value_.map_->begin()); |
| break; |
| default: |
| break; |
| } |
| return {}; |
| } |
| |
| Value::const_iterator Value::end() const { |
| switch (type()) { |
| case arrayValue: |
| case objectValue: |
| if (value_.map_) |
| return const_iterator(value_.map_->end()); |
| break; |
| default: |
| break; |
| } |
| return {}; |
| } |
| |
| Value::iterator Value::begin() { |
| switch (type()) { |
| case arrayValue: |
| case objectValue: |
| if (value_.map_) |
| return iterator(value_.map_->begin()); |
| break; |
| default: |
| break; |
| } |
| return iterator(); |
| } |
| |
| Value::iterator Value::end() { |
| switch (type()) { |
| case arrayValue: |
| case objectValue: |
| if (value_.map_) |
| return iterator(value_.map_->end()); |
| break; |
| default: |
| break; |
| } |
| return iterator(); |
| } |
| |
| // class PathArgument |
| // ////////////////////////////////////////////////////////////////// |
| |
| PathArgument::PathArgument() = 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 |