aboutsummaryrefslogtreecommitdiff
path: root/src/lib_json/json_value.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib_json/json_value.cpp')
-rw-r--r--src/lib_json/json_value.cpp1356
1 files changed, 756 insertions, 600 deletions
diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp
index b73deac..0872ff5 100644
--- a/src/lib_json/json_value.cpp
+++ b/src/lib_json/json_value.cpp
@@ -1,4 +1,4 @@
-// Copyright 2011 Baptiste Lepilleur
+// 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
@@ -7,23 +7,55 @@
#include <json/assertions.h>
#include <json/value.h>
#include <json/writer.h>
-#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-#include "json_batchallocator.h"
-#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <math.h>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
#include <sstream>
#include <utility>
-#include <cstring>
-#include <cassert>
-#ifdef JSON_USE_CPPTL
-#include <cpptl/conststring.h>
+
+// 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
-#include <cstddef> // size_t
#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
@@ -33,37 +65,34 @@ namespace Json {
#else
#define ALIGNAS(byte_alignment)
#endif
-static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
-const unsigned char& kNullRef = kNull[0];
-const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
-const Int Value::minInt = Int(~(UInt(-1) / 2));
-const Int Value::maxInt = Int(UInt(-1) / 2);
-const UInt Value::maxUInt = UInt(-1);
-#if defined(JSON_HAS_INT64)
-const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
-const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
-const UInt64 Value::maxUInt64 = UInt64(-1);
-// The constant is hard-coded because some compiler have trouble
-// converting Value::maxUInt64 to a double correctly (AIX/xlC).
-// Assumes that UInt64 is a 64 bits integer.
-static const double maxUInt64AsDouble = 18446744073709551615.0;
-#endif // defined(JSON_HAS_INT64)
-const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
-const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
-const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+// 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();
-/// Unknown size marker
-static const unsigned int unknown = (unsigned)-1;
+// 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) {
- return d >= min && d <= 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 + Int64(value & 1);
+ return static_cast<double>(Int64(value / 2)) * 2.0 +
+ static_cast<double>(Int64(value & 1));
}
template <typename T> static inline double integerToDouble(T value) {
@@ -83,28 +112,76 @@ static inline bool InRange(double d, T min, U max) {
* computed using strlen(value).
* @return Pointer on the duplicate instance of string.
*/
-static inline char* duplicateStringValue(const char* value,
- unsigned int length = unknown) {
- if (length == unknown)
- length = (unsigned int)strlen(value);
-
+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 >= (unsigned)Value::maxInt)
+ if (length >= static_cast<size_t>(Value::maxInt))
length = Value::maxInt - 1;
- char* newString = static_cast<char*>(malloc(length + 1));
- JSON_ASSERT_MESSAGE(newString != 0,
- "in Json::Value::duplicateStringValue(): "
+ 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;
}
-/** Free the string duplicated by duplicateStringValue().
+/* Record the length as a prefix.
*/
-static inline void releaseStringValue(char* value) { free(value); }
+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
@@ -116,41 +193,34 @@ static inline void releaseStringValue(char* value) { free(value); }
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
#if !defined(JSON_IS_AMALGAMATION)
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-#include "json_internalarray.inl"
-#include "json_internalmap.inl"
-#endif // JSON_VALUE_USE_INTERNAL_MAP
#include "json_valueiterator.inl"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CommentInfo
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-Value::CommentInfo::CommentInfo() : comment_(0) {}
-
-Value::CommentInfo::~CommentInfo() {
- if (comment_)
- releaseStringValue(comment_);
+#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);
}
-
-void Value::CommentInfo::setComment(const char* text) {
- if (comment_)
- releaseStringValue(comment_);
- JSON_ASSERT(text != 0);
- JSON_ASSERT_MESSAGE(
- text[0] == '\0' || text[0] == '/',
- "in Json::Value::setComment(): Comments must start with /");
- // It seems that /**/ style comments are acceptable as well.
- comment_ = duplicateStringValue(text);
+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
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
@@ -159,29 +229,49 @@ void Value::CommentInfo::setComment(const char* text) {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-// Notes: index_ indicates if the string was allocated when
+// Notes: policy_ indicates if the string was allocated when
// a string is stored.
-Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {}
+Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
-Value::CZString::CZString(const char* cstr, DuplicationPolicy allocate)
- : cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr),
- index_(allocate) {}
+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.index_ != noDuplication && other.cstr_ != 0
- ? duplicateStringValue(other.cstr_)
- : other.cstr_),
- index_(other.cstr_
- ? static_cast<ArrayIndex>(other.index_ == noDuplication
- ? noDuplication : duplicate)
- : other.index_) {}
+Value::CZString::CZString(const CZString& other) {
+ cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
+ ? duplicateStringValue(other.cstr_, other.storage_.length_)
+ : other.cstr_);
+ storage_.policy_ =
+ static_cast<unsigned>(
+ other.cstr_
+ ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
+ noDuplication
+ ? noDuplication
+ : duplicate)
+ : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
+ 3U;
+ storage_.length_ = other.storage_.length_;
+}
+
+Value::CZString::CZString(CZString&& other)
+ : cstr_(other.cstr_), index_(other.index_) {
+ other.cstr_ = nullptr;
+}
Value::CZString::~CZString() {
- if (cstr_ && index_ == duplicate)
- releaseStringValue(const_cast<char*>(cstr_));
+ 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) {
@@ -189,30 +279,58 @@ void Value::CZString::swap(CZString& other) {
std::swap(index_, other.index_);
}
-Value::CZString& Value::CZString::operator=(CZString other) {
- swap(other);
+Value::CZString& Value::CZString::operator=(const CZString& other) {
+ cstr_ = other.cstr_;
+ index_ = other.index_;
+ return *this;
+}
+
+Value::CZString& Value::CZString::operator=(CZString&& other) {
+ cstr_ = other.cstr_;
+ index_ = other.index_;
+ other.cstr_ = nullptr;
return *this;
}
bool Value::CZString::operator<(const CZString& other) const {
- if (cstr_)
- return strcmp(cstr_, other.cstr_) < 0;
- return index_ < other.index_;
+ 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 strcmp(cstr_, other.cstr_) == 0;
- return index_ == other.index_;
+ 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_; }
-
-bool Value::CZString::isStaticString() const { return index_ == noDuplication; }
-
-#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
+// 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;
+}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
@@ -227,6 +345,7 @@ bool Value::CZString::isStaticString() const { return index_ == noDuplication; }
* This optimization is used in ValueInternalMap fast allocator.
*/
Value::Value(ValueType type) {
+ static char const emptyString[] = "";
initBasic(type);
switch (type) {
case nullValue:
@@ -239,21 +358,13 @@ Value::Value(ValueType type) {
value_.real_ = 0.0;
break;
case stringValue:
- value_.string_ = 0;
+ // allocated_ == false, so this is safe.
+ value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
case arrayValue:
case objectValue:
value_.map_ = new ObjectValues();
break;
-#else
- case arrayValue:
- value_.array_ = arrayAllocator()->newArray();
- break;
- case objectValue:
- value_.map_ = mapAllocator()->newMap();
- break;
-#endif
case booleanValue:
value_.bool_ = false;
break;
@@ -289,19 +400,22 @@ Value::Value(double value) {
Value::Value(const char* value) {
initBasic(stringValue, true);
- value_.string_ = duplicateStringValue(value);
+ JSON_ASSERT_MESSAGE(value != nullptr,
+ "Null Value Passed to Value Constructor");
+ value_.string_ = duplicateAndPrefixStringValue(
+ value, static_cast<unsigned>(strlen(value)));
}
-Value::Value(const char* beginValue, const char* endValue) {
+Value::Value(const char* begin, const char* end) {
initBasic(stringValue, true);
value_.string_ =
- duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue));
+ duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
}
-Value::Value(const std::string& value) {
+Value::Value(const String& value) {
initBasic(stringValue, true);
- value_.string_ =
- duplicateStringValue(value.c_str(), (unsigned int)value.length());
+ value_.string_ = duplicateAndPrefixStringValue(
+ value.data(), static_cast<unsigned>(value.length()));
}
Value::Value(const StaticString& value) {
@@ -309,120 +423,61 @@ Value::Value(const StaticString& value) {
value_.string_ = const_cast<char*>(value.c_str());
}
-#ifdef JSON_USE_CPPTL
-Value::Value(const CppTL::ConstString& value) {
- initBasic(stringValue, true);
- value_.string_ = duplicateStringValue(value, value.length());
-}
-#endif
-
Value::Value(bool value) {
initBasic(booleanValue);
value_.bool_ = value;
}
-Value::Value(const Value& other)
- : type_(other.type_), allocated_(false)
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- ,
- itemIsUsed_(0)
-#endif
- ,
- comments_(0), start_(other.start_), limit_(other.limit_) {
- switch (type_) {
- case nullValue:
- case intValue:
- case uintValue:
- case realValue:
- case booleanValue:
- value_ = other.value_;
- break;
- case stringValue:
- if (other.value_.string_) {
- value_.string_ = duplicateStringValue(other.value_.string_);
- allocated_ = true;
- } else {
- value_.string_ = 0;
- allocated_ = false;
- }
- break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- value_.map_ = new ObjectValues(*other.value_.map_);
- break;
-#else
- case arrayValue:
- value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_);
- break;
- case objectValue:
- value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_);
- break;
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- if (other.comments_) {
- comments_ = new CommentInfo[numberOfCommentPlacement];
- for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
- const CommentInfo& otherComment = other.comments_[comment];
- if (otherComment.comment_)
- comments_[comment].setComment(otherComment.comment_);
- }
- }
+Value::Value(const Value& other) {
+ dupPayload(other);
+ dupMeta(other);
+}
+
+Value::Value(Value&& other) {
+ initBasic(nullValue);
+ swap(other);
}
Value::~Value() {
- switch (type_) {
- case nullValue:
- case intValue:
- case uintValue:
- case realValue:
- case booleanValue:
- break;
- case stringValue:
- if (allocated_)
- releaseStringValue(value_.string_);
- break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- delete value_.map_;
- break;
-#else
- case arrayValue:
- arrayAllocator()->destructArray(value_.array_);
- break;
- case objectValue:
- mapAllocator()->destructMap(value_.map_);
- break;
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
+ releasePayload();
+ value_.uint_ = 0;
+}
- if (comments_)
- delete[] comments_;
+Value& Value::operator=(const Value& other) {
+ Value(other).swap(*this);
+ return *this;
}
-Value& Value::operator=(Value other) {
- swap(other);
+Value& Value::operator=(Value&& other) {
+ other.swap(*this);
return *this;
}
-void Value::swap(Value& other) {
- ValueType temp = type_;
- type_ = other.type_;
- other.type_ = temp;
+void Value::swapPayload(Value& other) {
+ std::swap(bits_, other.bits_);
std::swap(value_, other.value_);
- int temp2 = allocated_;
- allocated_ = other.allocated_;
- other.allocated_ = temp2;
+}
+
+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_);
}
-ValueType Value::type() const { return type_; }
+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)
@@ -433,10 +488,10 @@ int Value::compare(const Value& other) const {
}
bool Value::operator<(const Value& other) const {
- int typeDelta = type_ - other.type_;
+ int typeDelta = type() - other.type();
if (typeDelta)
- return typeDelta < 0 ? true : false;
- switch (type_) {
+ return typeDelta < 0;
+ switch (type()) {
case nullValue:
return false;
case intValue:
@@ -447,24 +502,35 @@ bool Value::operator<(const Value& other) const {
return value_.real_ < other.value_.real_;
case booleanValue:
return value_.bool_ < other.value_.bool_;
- case stringValue:
- return (value_.string_ == 0 && other.value_.string_) ||
- (other.value_.string_ && value_.string_ &&
- strcmp(value_.string_, other.value_.string_) < 0);
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case stringValue: {
+ if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
+ return other.value_.string_ != nullptr;
+ }
+ unsigned this_len;
+ unsigned other_len;
+ char const* this_str;
+ char const* other_str;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+ &this_str);
+ decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+ &other_str);
+ unsigned min_len = std::min<unsigned>(this_len, other_len);
+ JSON_ASSERT(this_str && other_str);
+ int comp = memcmp(this_str, other_str, min_len);
+ if (comp < 0)
+ return true;
+ if (comp > 0)
+ return false;
+ return (this_len < other_len);
+ }
case arrayValue:
case objectValue: {
- int delta = int(value_.map_->size() - other.value_.map_->size());
- if (delta)
- return delta < 0;
+ auto thisSize = value_.map_->size();
+ auto otherSize = other.value_.map_->size();
+ if (thisSize != otherSize)
+ return thisSize < otherSize;
return (*value_.map_) < (*other.value_.map_);
}
-#else
- case arrayValue:
- return value_.array_->compare(*(other.value_.array_)) < 0;
- case objectValue:
- return value_.map_->compare(*(other.value_.map_)) < 0;
-#endif
default:
JSON_ASSERT_UNREACHABLE;
}
@@ -478,14 +544,9 @@ 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_ )
- // GCC 2.95.3 says:
- // attempt to take address of bit-field structure member `Json::Value::type_'
- // Beats me, but a temp solves the problem.
- int temp = other.type_;
- if (type_ != temp)
+ if (type() != other.type())
return false;
- switch (type_) {
+ switch (type()) {
case nullValue:
return true;
case intValue:
@@ -496,21 +557,28 @@ bool Value::operator==(const Value& other) const {
return value_.real_ == other.value_.real_;
case booleanValue:
return value_.bool_ == other.value_.bool_;
- case stringValue:
- return (value_.string_ == other.value_.string_) ||
- (other.value_.string_ && value_.string_ &&
- strcmp(value_.string_, other.value_.string_) == 0);
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ 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_);
-#else
- case arrayValue:
- return value_.array_->compare(*(other.value_.array_)) == 0;
- case objectValue:
- return value_.map_->compare(*(other.value_.map_)) == 0;
-#endif
default:
JSON_ASSERT_UNREACHABLE;
}
@@ -520,17 +588,56 @@ bool Value::operator==(const Value& other) const {
bool Value::operator!=(const Value& other) const { return !(*this == other); }
const char* Value::asCString() const {
- JSON_ASSERT_MESSAGE(type_ == stringValue,
+ JSON_ASSERT_MESSAGE(type() == stringValue,
"in Json::Value::asCString(): requires stringValue");
- return value_.string_;
+ 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;
}
-std::string Value::asString() const {
- switch (type_) {
+String Value::asString() const {
+ switch (type()) {
case nullValue:
return "";
- case stringValue:
- return value_.string_ ? value_.string_ : "";
+ 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:
@@ -544,14 +651,8 @@ std::string Value::asString() const {
}
}
-#ifdef JSON_USE_CPPTL
-CppTL::ConstString Value::asConstString() const {
- return CppTL::ConstString(asString().c_str());
-}
-#endif
-
Value::Int Value::asInt() const {
- switch (type_) {
+ switch (type()) {
case intValue:
JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
return Int(value_.int_);
@@ -573,7 +674,7 @@ Value::Int Value::asInt() const {
}
Value::UInt Value::asUInt() const {
- switch (type_) {
+ switch (type()) {
case intValue:
JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
return UInt(value_.int_);
@@ -597,7 +698,7 @@ Value::UInt Value::asUInt() const {
#if defined(JSON_HAS_INT64)
Value::Int64 Value::asInt64() const {
- switch (type_) {
+ switch (type()) {
case intValue:
return Int64(value_.int_);
case uintValue:
@@ -618,7 +719,7 @@ Value::Int64 Value::asInt64() const {
}
Value::UInt64 Value::asUInt64() const {
- switch (type_) {
+ switch (type()) {
case intValue:
JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
return UInt64(value_.int_);
@@ -656,7 +757,7 @@ LargestUInt Value::asLargestUInt() const {
}
double Value::asDouble() const {
- switch (type_) {
+ switch (type()) {
case intValue:
return static_cast<double>(value_.int_);
case uintValue:
@@ -678,21 +779,22 @@ double Value::asDouble() const {
}
float Value::asFloat() const {
- switch (type_) {
+ 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)
- return integerToDouble(value_.uint_);
+ // 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;
+ return value_.bool_ ? 1.0F : 0.0F;
default:
break;
}
@@ -700,17 +802,20 @@ float Value::asFloat() const {
}
bool Value::asBool() const {
- switch (type_) {
+ switch (type()) {
case booleanValue:
return value_.bool_;
case nullValue:
return false;
case intValue:
- return value_.int_ ? true : false;
+ return value_.int_ != 0;
case uintValue:
- return value_.uint_ ? true : false;
- case realValue:
- return value_.real_ ? true : false;
+ 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;
}
@@ -721,30 +826,30 @@ bool Value::isConvertibleTo(ValueType other) const {
switch (other) {
case nullValue:
return (isNumeric() && asDouble() == 0.0) ||
- (type_ == booleanValue && value_.bool_ == false) ||
- (type_ == stringValue && asString() == "") ||
- (type_ == arrayValue && value_.map_->size() == 0) ||
- (type_ == objectValue && value_.map_->size() == 0) ||
- type_ == nullValue;
+ (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;
+ (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;
+ (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
+ type() == booleanValue || type() == nullValue;
case realValue:
- return isNumeric() || type_ == booleanValue || type_ == nullValue;
+ return isNumeric() || type() == booleanValue || type() == nullValue;
case booleanValue:
- return isNumeric() || type_ == booleanValue || type_ == nullValue;
+ return isNumeric() || type() == booleanValue || type() == nullValue;
case stringValue:
- return isNumeric() || type_ == booleanValue || type_ == stringValue ||
- type_ == nullValue;
+ return isNumeric() || type() == booleanValue || type() == stringValue ||
+ type() == nullValue;
case arrayValue:
- return type_ == arrayValue || type_ == nullValue;
+ return type() == arrayValue || type() == nullValue;
case objectValue:
- return type_ == objectValue || type_ == nullValue;
+ return type() == objectValue || type() == nullValue;
}
JSON_ASSERT_UNREACHABLE;
return false;
@@ -752,7 +857,7 @@ bool Value::isConvertibleTo(ValueType other) const {
/// Number of values in array or object
ArrayIndex Value::size() const {
- switch (type_) {
+ switch (type()) {
case nullValue:
case intValue:
case uintValue:
@@ -760,7 +865,6 @@ ArrayIndex Value::size() const {
case booleanValue:
case stringValue:
return 0;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
case arrayValue: // size of the array is highest index + 1
if (!value_.map_->empty()) {
ObjectValues::const_iterator itLast = value_.map_->end();
@@ -770,12 +874,6 @@ ArrayIndex Value::size() const {
return 0;
case objectValue:
return ArrayIndex(value_.map_->size());
-#else
- case arrayValue:
- return Int(value_.array_->size());
- case objectValue:
- return Int(value_.map_->size());
-#endif
}
JSON_ASSERT_UNREACHABLE;
return 0; // unreachable;
@@ -783,78 +881,60 @@ ArrayIndex Value::size() const {
bool Value::empty() const {
if (isNull() || isArray() || isObject())
- return size() == 0u;
- else
- return false;
+ return size() == 0U;
+ return false;
}
-bool Value::operator!() const { return isNull(); }
+Value::operator bool() const { return !isNull(); }
void Value::clear() {
- JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||
- type_ == objectValue,
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
+ type() == objectValue,
"in Json::Value::clear(): requires complex value");
start_ = 0;
limit_ = 0;
- switch (type_) {
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- value_.map_->clear();
- break;
-#else
+ switch (type()) {
case arrayValue:
- value_.array_->clear();
- break;
case objectValue:
value_.map_->clear();
break;
-#endif
default:
break;
}
}
void Value::resize(ArrayIndex newSize) {
- JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue,
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
"in Json::Value::resize(): requires arrayValue");
- if (type_ == nullValue)
+ if (type() == nullValue)
*this = Value(arrayValue);
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
ArrayIndex oldSize = size();
if (newSize == 0)
clear();
else if (newSize > oldSize)
- (*this)[newSize - 1];
+ this->operator[](newSize - 1);
else {
for (ArrayIndex index = newSize; index < oldSize; ++index) {
value_.map_->erase(index);
}
- assert(size() == newSize);
+ JSON_ASSERT(size() == newSize);
}
-#else
- value_.array_->resize(newSize);
-#endif
}
Value& Value::operator[](ArrayIndex index) {
JSON_ASSERT_MESSAGE(
- type_ == nullValue || type_ == arrayValue,
+ type() == nullValue || type() == arrayValue,
"in Json::Value::operator[](ArrayIndex): requires arrayValue");
- if (type_ == nullValue)
+ if (type() == nullValue)
*this = Value(arrayValue);
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
CZString key(index);
- ObjectValues::iterator it = value_.map_->lower_bound(key);
+ auto it = value_.map_->lower_bound(key);
if (it != value_.map_->end() && (*it).first == key)
return (*it).second;
- ObjectValues::value_type defaultValue(key, null);
+ ObjectValues::value_type defaultValue(key, nullSingleton());
it = value_.map_->insert(it, defaultValue);
return (*it).second;
-#else
- return value_.array_->resolveReference(index);
-#endif
}
Value& Value::operator[](int index) {
@@ -866,20 +946,15 @@ Value& Value::operator[](int index) {
const Value& Value::operator[](ArrayIndex index) const {
JSON_ASSERT_MESSAGE(
- type_ == nullValue || type_ == arrayValue,
+ type() == nullValue || type() == arrayValue,
"in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
- if (type_ == nullValue)
- return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ if (type() == nullValue)
+ return nullSingleton();
CZString key(index);
ObjectValues::const_iterator it = value_.map_->find(key);
if (it == value_.map_->end())
- return null;
+ return nullSingleton();
return (*it).second;
-#else
- Value* value = value_.array_->find(index);
- return value ? *value : null;
-#endif
}
const Value& Value::operator[](int index) const {
@@ -889,214 +964,304 @@ const Value& Value::operator[](int index) const {
return (*this)[ArrayIndex(index)];
}
-Value& Value::operator[](const char* key) {
- return resolveReference(key, false);
-}
-
void Value::initBasic(ValueType type, bool allocated) {
- type_ = type;
- allocated_ = allocated;
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- itemIsUsed_ = 0;
-#endif
- comments_ = 0;
+ setType(type);
+ setIsAllocated(allocated);
+ comments_ = Comments{};
start_ = 0;
limit_ = 0;
}
-Value& Value::resolveReference(const char* key, bool isStatic) {
+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,
+ type() == nullValue || type() == objectValue,
"in Json::Value::resolveReference(): requires objectValue");
- if (type_ == nullValue)
+ if (type() == nullValue)
*this = Value(objectValue);
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString actualKey(
- key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy);
- ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+ 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, null);
+ 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;
-#else
- return value_.map_->resolveReference(key, isStatic);
-#endif
}
Value Value::get(ArrayIndex index, const Value& defaultValue) const {
const Value* value = &((*this)[index]);
- return value == &null ? defaultValue : *value;
+ return value == &nullSingleton() ? defaultValue : *value;
}
bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
-const Value& Value::operator[](const char* key) const {
- JSON_ASSERT_MESSAGE(
- type_ == nullValue || type_ == objectValue,
- "in Json::Value::operator[](char const*)const: requires objectValue");
- if (type_ == nullValue)
- return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString actualKey(key, CZString::noDuplication);
+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 null;
- return (*it).second;
-#else
- const Value* value = value_.map_->find(key);
- return value ? *value : null;
-#endif
+ 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 std::string& key) {
- return (*this)[key.c_str()];
+Value& Value::operator[](const char* key) {
+ return resolveReference(key, key + strlen(key));
}
-const Value& Value::operator[](const std::string& key) const {
- return (*this)[key.c_str()];
+Value& Value::operator[](const String& key) {
+ return resolveReference(key.data(), key.data() + key.length());
}
Value& Value::operator[](const StaticString& key) {
- return resolveReference(key, true);
+ return resolveReference(key.c_str());
}
-#ifdef JSON_USE_CPPTL
-Value& Value::operator[](const CppTL::ConstString& key) {
- return (*this)[key.c_str()];
-}
+Value& Value::append(const Value& value) { return append(Value(value)); }
-const Value& Value::operator[](const CppTL::ConstString& key) const {
- return (*this)[key.c_str()];
+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;
}
-#endif
-Value& Value::append(const Value& value) { return (*this)[size()] = value; }
+bool Value::insert(ArrayIndex index, const Value& newValue) {
+ return insert(index, Value(newValue));
+}
-Value Value::get(const char* key, const Value& defaultValue) const {
- const Value* value = &((*this)[key]);
- return value == &null ? defaultValue : *value;
+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(const std::string& key, const Value& defaultValue) const {
- return get(key.c_str(), defaultValue);
+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);
}
-Value Value::removeMember(const char* key) {
- JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
- "in Json::Value::removeMember(): requires objectValue");
- if (type_ == nullValue)
- return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString actualKey(key, CZString::noDuplication);
- ObjectValues::iterator it = value_.map_->find(actualKey);
+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 null;
- Value old(it->second);
+ return false;
+ if (removed)
+ *removed = std::move(it->second);
value_.map_->erase(it);
- return old;
-#else
- Value* value = value_.map_->find(key);
- if (value) {
- Value old(*value);
- value_.map_.remove(key);
- return old;
- } else {
- return null;
- }
-#endif
+ return true;
}
-
-Value Value::removeMember(const std::string& key) {
- return removeMember(key.c_str());
+bool Value::removeMember(const char* key, Value* removed) {
+ return removeMember(key, key + strlen(key), removed);
}
-
-#ifdef JSON_USE_CPPTL
-Value Value::get(const CppTL::ConstString& key,
- const Value& defaultValue) const {
- return get(key.c_str(), defaultValue);
+bool Value::removeMember(String const& key, Value* removed) {
+ return removeMember(key.data(), key.data() + key.length(), removed);
}
-#endif
+void Value::removeMember(const char* key) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+ "in Json::Value::removeMember(): requires objectValue");
+ if (type() == nullValue)
+ return;
-bool Value::isMember(const char* key) const {
- const Value* value = &((*this)[key]);
- return value != &null;
+ CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
+ value_.map_->erase(actualKey);
}
+void Value::removeMember(const String& key) { removeMember(key.c_str()); }
-bool Value::isMember(const std::string& key) const {
- return isMember(key.c_str());
+bool Value::removeIndex(ArrayIndex index, Value* removed) {
+ if (type() != arrayValue) {
+ return false;
+ }
+ CZString key(index);
+ auto it = value_.map_->find(key);
+ if (it == value_.map_->end()) {
+ return false;
+ }
+ if (removed)
+ *removed = it->second;
+ ArrayIndex oldSize = size();
+ // shift left all items left, into the place of the "removed"
+ for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
+ CZString keey(i);
+ (*value_.map_)[keey] = (*this)[i + 1];
+ }
+ // erase the last one ("leftover")
+ CZString keyLast(oldSize - 1);
+ auto itLast = value_.map_->find(keyLast);
+ value_.map_->erase(itLast);
+ return true;
}
-#ifdef JSON_USE_CPPTL
-bool Value::isMember(const CppTL::ConstString& key) const {
- return isMember(key.c_str());
+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());
}
-#endif
Value::Members Value::getMemberNames() const {
JSON_ASSERT_MESSAGE(
- type_ == nullValue || type_ == objectValue,
+ type() == nullValue || type() == objectValue,
"in Json::Value::getMemberNames(), value must be objectValue");
- if (type_ == nullValue)
+ if (type() == nullValue)
return Value::Members();
Members members;
members.reserve(value_.map_->size());
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
ObjectValues::const_iterator it = value_.map_->begin();
ObjectValues::const_iterator itEnd = value_.map_->end();
- for (; it != itEnd; ++it)
- members.push_back(std::string((*it).first.c_str()));
-#else
- ValueInternalMap::IteratorState it;
- ValueInternalMap::IteratorState itEnd;
- value_.map_->makeBeginIterator(it);
- value_.map_->makeEndIterator(itEnd);
- for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it))
- members.push_back(std::string(ValueInternalMap::key(it)));
-#endif
+ for (; it != itEnd; ++it) {
+ members.push_back(String((*it).first.data(), (*it).first.length()));
+ }
return members;
}
-//
-//# ifdef JSON_USE_CPPTL
-// EnumMemberNames
-// Value::enumMemberNames() const
-//{
-// if ( type_ == objectValue )
-// {
-// return CppTL::Enum::any( CppTL::Enum::transform(
-// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
-// MemberNamesTransform() ) );
-// }
-// return EnumMemberNames();
-//}
-//
-//
-// EnumValues
-// Value::enumValues() const
-//{
-// if ( type_ == objectValue || type_ == arrayValue )
-// return CppTL::Enum::anyValues( *(value_.map_),
-// CppTL::Type<const Value &>() );
-// return EnumValues();
-//}
-//
-//# endif
static bool IsIntegral(double d) {
double integral_part;
return modf(d, &integral_part) == 0.0;
}
-bool Value::isNull() const { return type_ == nullValue; }
+bool Value::isNull() const { return type() == nullValue; }
-bool Value::isBool() const { return type_ == booleanValue; }
+bool Value::isBool() const { return type() == booleanValue; }
bool Value::isInt() const {
- switch (type_) {
+ 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:
@@ -1109,11 +1274,19 @@ bool Value::isInt() const {
}
bool Value::isUInt() const {
- switch (type_) {
+ 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_);
@@ -1125,7 +1298,7 @@ bool Value::isUInt() const {
bool Value::isInt64() const {
#if defined(JSON_HAS_INT64)
- switch (type_) {
+ switch (type()) {
case intValue:
return true;
case uintValue:
@@ -1145,7 +1318,7 @@ bool Value::isInt64() const {
bool Value::isUInt64() const {
#if defined(JSON_HAS_INT64)
- switch (type_) {
+ switch (type()) {
case intValue:
return value_.int_ >= 0;
case uintValue:
@@ -1164,140 +1337,145 @@ bool Value::isUInt64() const {
}
bool Value::isIntegral() const {
+ switch (type()) {
+ case intValue:
+ case uintValue:
+ return true;
+ case realValue:
#if defined(JSON_HAS_INT64)
- return isInt64() || isUInt64();
+ // 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 isInt() || isUInt();
-#endif
+ return value_.real_ >= minInt && value_.real_ <= maxUInt &&
+ IsIntegral(value_.real_);
+#endif // JSON_HAS_INT64
+ default:
+ break;
+ }
+ return false;
}
-bool Value::isDouble() const { return type_ == realValue || isIntegral(); }
+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::isNumeric() const { return isIntegral() || isDouble(); }
+bool Value::isArray() const { return type() == arrayValue; }
-bool Value::isString() const { return type_ == stringValue; }
+bool Value::isObject() const { return type() == objectValue; }
-bool Value::isArray() const { return type_ == arrayValue; }
+Value::Comments::Comments(const Comments& that)
+ : ptr_{cloneUnique(that.ptr_)} {}
-bool Value::isObject() const { return type_ == objectValue; }
+Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {}
-void Value::setComment(const char* comment, CommentPlacement placement) {
- if (!comments_)
- comments_ = new CommentInfo[numberOfCommentPlacement];
- comments_[placement].setComment(comment);
+Value::Comments& Value::Comments::operator=(const Comments& that) {
+ ptr_ = cloneUnique(that.ptr_);
+ return *this;
}
-void Value::setComment(const std::string& comment, CommentPlacement placement) {
- setComment(comment.c_str(), placement);
+Value::Comments& Value::Comments::operator=(Comments&& that) {
+ ptr_ = std::move(that.ptr_);
+ return *this;
+}
+
+bool Value::Comments::has(CommentPlacement slot) const {
+ return ptr_ && !(*ptr_)[slot].empty();
+}
+
+String Value::Comments::get(CommentPlacement slot) const {
+ if (!ptr_)
+ return {};
+ return (*ptr_)[slot];
+}
+
+void Value::Comments::set(CommentPlacement slot, String comment) {
+ if (!ptr_) {
+ ptr_ = std::unique_ptr<Array>(new Array());
+ }
+ // check comments array boundry.
+ if (slot < CommentPlacement::numberOfCommentPlacement) {
+ (*ptr_)[slot] = std::move(comment);
+ }
+}
+
+void Value::setComment(String comment, CommentPlacement placement) {
+ if (!comment.empty() && (comment.back() == '\n')) {
+ // Always discard trailing newline, to aid indentation.
+ comment.pop_back();
+ }
+ JSON_ASSERT(!comment.empty());
+ JSON_ASSERT_MESSAGE(
+ comment[0] == '\0' || comment[0] == '/',
+ "in Json::Value::setComment(): Comments must start with /");
+ comments_.set(placement, std::move(comment));
}
bool Value::hasComment(CommentPlacement placement) const {
- return comments_ != 0 && comments_[placement].comment_ != 0;
+ return comments_.has(placement);
}
-std::string Value::getComment(CommentPlacement placement) const {
- if (hasComment(placement))
- return comments_[placement].comment_;
- return "";
+String Value::getComment(CommentPlacement placement) const {
+ return comments_.get(placement);
}
-void Value::setOffsetStart(size_t start) { start_ = start; }
+void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
+
+void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }
-void Value::setOffsetLimit(size_t limit) { limit_ = limit; }
+ptrdiff_t Value::getOffsetStart() const { return start_; }
-size_t Value::getOffsetStart() const { return start_; }
+ptrdiff_t Value::getOffsetLimit() const { return limit_; }
-size_t Value::getOffsetLimit() const { return limit_; }
+String Value::toStyledString() const {
+ StreamWriterBuilder builder;
-std::string Value::toStyledString() const {
- StyledWriter writer;
- return writer.write(*this);
+ String out = this->hasComment(commentBefore) ? "\n" : "";
+ out += Json::writeString(builder, *this);
+ out += '\n';
+
+ return out;
}
Value::const_iterator Value::begin() const {
- switch (type_) {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if (value_.array_) {
- ValueInternalArray::IteratorState it;
- value_.array_->makeBeginIterator(it);
- return const_iterator(it);
- }
- break;
- case objectValue:
- if (value_.map_) {
- ValueInternalMap::IteratorState it;
- value_.map_->makeBeginIterator(it);
- return const_iterator(it);
- }
- break;
-#else
+ switch (type()) {
case arrayValue:
case objectValue:
if (value_.map_)
return const_iterator(value_.map_->begin());
break;
-#endif
default:
break;
}
- return const_iterator();
+ return {};
}
Value::const_iterator Value::end() const {
- switch (type_) {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if (value_.array_) {
- ValueInternalArray::IteratorState it;
- value_.array_->makeEndIterator(it);
- return const_iterator(it);
- }
- break;
- case objectValue:
- if (value_.map_) {
- ValueInternalMap::IteratorState it;
- value_.map_->makeEndIterator(it);
- return const_iterator(it);
- }
- break;
-#else
+ switch (type()) {
case arrayValue:
case objectValue:
if (value_.map_)
return const_iterator(value_.map_->end());
break;
-#endif
default:
break;
}
- return const_iterator();
+ return {};
}
Value::iterator Value::begin() {
- switch (type_) {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if (value_.array_) {
- ValueInternalArray::IteratorState it;
- value_.array_->makeBeginIterator(it);
- return iterator(it);
- }
- break;
- case objectValue:
- if (value_.map_) {
- ValueInternalMap::IteratorState it;
- value_.map_->makeBeginIterator(it);
- return iterator(it);
- }
- break;
-#else
+ switch (type()) {
case arrayValue:
case objectValue:
if (value_.map_)
return iterator(value_.map_->begin());
break;
-#endif
default:
break;
}
@@ -1305,29 +1483,12 @@ Value::iterator Value::begin() {
}
Value::iterator Value::end() {
- switch (type_) {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if (value_.array_) {
- ValueInternalArray::IteratorState it;
- value_.array_->makeEndIterator(it);
- return iterator(it);
- }
- break;
- case objectValue:
- if (value_.map_) {
- ValueInternalMap::IteratorState it;
- value_.map_->makeEndIterator(it);
- return iterator(it);
- }
- break;
-#else
+ switch (type()) {
case arrayValue:
case objectValue:
if (value_.map_)
return iterator(value_.map_->end());
break;
-#endif
default:
break;
}
@@ -1337,27 +1498,23 @@ Value::iterator Value::end() {
// class PathArgument
// //////////////////////////////////////////////////////////////////
-PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {}
+PathArgument::PathArgument() = default;
PathArgument::PathArgument(ArrayIndex index)
- : key_(), index_(index), kind_(kindIndex) {}
+ : index_(index), kind_(kindIndex) {}
-PathArgument::PathArgument(const char* key)
- : key_(key), index_(), kind_(kindKey) {}
+PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {}
-PathArgument::PathArgument(const std::string& key)
- : key_(key.c_str()), index_(), kind_(kindKey) {}
+PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {}
// class Path
// //////////////////////////////////////////////////////////////////
-Path::Path(const std::string& path,
- const PathArgument& a1,
- const PathArgument& a2,
- const PathArgument& a3,
- const PathArgument& a4,
+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);
@@ -1366,10 +1523,10 @@ Path::Path(const std::string& path,
makePath(path, in);
}
-void Path::makePath(const std::string& path, const InArgs& in) {
+void Path::makePath(const String& path, const InArgs& in) {
const char* current = path.c_str();
const char* end = current + path.length();
- InArgs::const_iterator itInArg = in.begin();
+ auto itInArg = in.begin();
while (current != end) {
if (*current == '[') {
++current;
@@ -1381,24 +1538,23 @@ void Path::makePath(const std::string& path, const InArgs& in) {
index = index * 10 + ArrayIndex(*current - '0');
args_.push_back(index);
}
- if (current == end || *current++ != ']')
+ if (current == end || *++current != ']')
invalidPath(path, int(current - path.c_str()));
} else if (*current == '%') {
addPathInArg(path, in, itInArg, PathArgument::kindKey);
++current;
- } else if (*current == '.') {
+ } else if (*current == '.' || *current == ']') {
++current;
} else {
const char* beginName = current;
while (current != end && !strchr("[.", *current))
++current;
- args_.push_back(std::string(beginName, current));
+ args_.push_back(String(beginName, current));
}
}
}
-void Path::addPathInArg(const std::string& /*path*/,
- const InArgs& in,
+void Path::addPathInArg(const String& /*path*/, const InArgs& in,
InArgs::const_iterator& itInArg,
PathArgument::Kind kind) {
if (itInArg == in.end()) {
@@ -1406,31 +1562,33 @@ void Path::addPathInArg(const std::string& /*path*/,
} else if ((*itInArg)->kind_ != kind) {
// Error: bad argument type
} else {
- args_.push_back(**itInArg);
+ args_.push_back(**itInArg++);
}
}
-void Path::invalidPath(const std::string& /*path*/, int /*location*/) {
+void Path::invalidPath(const String& /*path*/, int /*location*/) {
// Error: invalid path.
}
const Value& Path::resolve(const Value& root) const {
const Value* node = &root;
- for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
- const PathArgument& arg = *it;
+ 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...
+ // 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::null) {
+ if (node == &Value::nullSingleton()) {
// Error: unable to resolve path (object has no member named '' at
// position...)
+ return Value::nullSingleton();
}
}
}
@@ -1439,8 +1597,7 @@ const Value& Path::resolve(const Value& root) const {
Value Path::resolve(const Value& root, const Value& defaultValue) const {
const Value* node = &root;
- for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
- const PathArgument& arg = *it;
+ for (const auto& arg : args_) {
if (arg.kind_ == PathArgument::kindIndex) {
if (!node->isArray() || !node->isValidIndex(arg.index_))
return defaultValue;
@@ -1449,7 +1606,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const {
if (!node->isObject())
return defaultValue;
node = &((*node)[arg.key_]);
- if (node == &Value::null)
+ if (node == &Value::nullSingleton())
return defaultValue;
}
}
@@ -1458,8 +1615,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const {
Value& Path::make(Value& root) const {
Value* node = &root;
- for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
- const PathArgument& arg = *it;
+ for (const auto& arg : args_) {
if (arg.kind_ == PathArgument::kindIndex) {
if (!node->isArray()) {
// Error: node is not an array at position ...