forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1634 lines
		
	
	
	
		
			47 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1634 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 <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
 | 
