mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-28 23:52:26 +00:00
1207 lines
27 KiB
C++
1207 lines
27 KiB
C++
#include <xrpl/beast/core/LexicalCast.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/json/detail/json_assert.h>
|
|
#include <xrpl/json/json_forwards.h>
|
|
#include <xrpl/json/json_value.h>
|
|
#include <xrpl/json/json_writer.h>
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
namespace Json {
|
|
|
|
Value const Value::null;
|
|
|
|
class DefaultValueAllocator : public ValueAllocator
|
|
{
|
|
public:
|
|
virtual ~DefaultValueAllocator() = default;
|
|
|
|
char*
|
|
makeMemberName(char const* memberName) override
|
|
{
|
|
return duplicateStringValue(memberName);
|
|
}
|
|
|
|
void
|
|
releaseMemberName(char* memberName) override
|
|
{
|
|
releaseStringValue(memberName);
|
|
}
|
|
|
|
char*
|
|
duplicateStringValue(char const* value, unsigned int length = unknown) override
|
|
{
|
|
//@todo investigate this old optimization
|
|
// if ( !value || value[0] == 0 )
|
|
// return 0;
|
|
|
|
if (length == unknown)
|
|
length = (value != nullptr) ? (unsigned int)strlen(value) : 0;
|
|
|
|
char* newString = static_cast<char*>(malloc(length + 1));
|
|
if (value != nullptr)
|
|
memcpy(newString, value, length);
|
|
newString[length] = 0;
|
|
return newString;
|
|
}
|
|
|
|
void
|
|
releaseStringValue(char* value) override
|
|
{
|
|
if (value != nullptr)
|
|
free(value);
|
|
}
|
|
};
|
|
|
|
static ValueAllocator*&
|
|
valueAllocator()
|
|
{
|
|
static ValueAllocator* valueAllocator = new DefaultValueAllocator;
|
|
return valueAllocator;
|
|
}
|
|
|
|
static struct DummyValueAllocatorInitializer
|
|
{
|
|
DummyValueAllocatorInitializer()
|
|
{
|
|
valueAllocator(); // ensure valueAllocator() statics are initialized
|
|
// before main().
|
|
}
|
|
} dummyValueAllocatorInitializer;
|
|
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
// class Value::CZString
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
// Notes: index_ indicates if the string was allocated when
|
|
// a string is stored.
|
|
|
|
Value::CZString::CZString(int index) : cstr_(0), index_(index)
|
|
{
|
|
}
|
|
|
|
Value::CZString::CZString(char const* cstr, DuplicationPolicy allocate)
|
|
: cstr_(allocate == duplicate ? valueAllocator()->makeMemberName(cstr) : cstr), index_(allocate)
|
|
{
|
|
}
|
|
|
|
Value::CZString::CZString(CZString const& other)
|
|
: cstr_(
|
|
other.index_ != noDuplication && other.cstr_ != 0
|
|
? valueAllocator()->makeMemberName(other.cstr_)
|
|
: other.cstr_)
|
|
, index_([&]() -> int {
|
|
if (!other.cstr_)
|
|
return other.index_;
|
|
return other.index_ == noDuplication ? noDuplication : duplicate;
|
|
}())
|
|
{
|
|
}
|
|
|
|
Value::CZString::~CZString()
|
|
{
|
|
if ((cstr_ != nullptr) && index_ == duplicate)
|
|
valueAllocator()->releaseMemberName(const_cast<char*>(cstr_));
|
|
}
|
|
|
|
bool
|
|
Value::CZString::operator<(CZString const& other) const
|
|
{
|
|
if ((cstr_ != nullptr) && (other.cstr_ != nullptr))
|
|
return strcmp(cstr_, other.cstr_) < 0;
|
|
|
|
return index_ < other.index_;
|
|
}
|
|
|
|
bool
|
|
Value::CZString::operator==(CZString const& other) const
|
|
{
|
|
if ((cstr_ != nullptr) && (other.cstr_ != nullptr))
|
|
return strcmp(cstr_, other.cstr_) == 0;
|
|
|
|
return index_ == other.index_;
|
|
}
|
|
|
|
int
|
|
Value::CZString::index() const
|
|
{
|
|
return index_;
|
|
}
|
|
|
|
char const*
|
|
Value::CZString::c_str() const
|
|
{
|
|
return cstr_;
|
|
}
|
|
|
|
bool
|
|
Value::CZString::isStaticString() const
|
|
{
|
|
return index_ == 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) : type_(type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case nullValue:
|
|
break;
|
|
|
|
case intValue:
|
|
case uintValue:
|
|
value_.int_ = 0;
|
|
break;
|
|
|
|
case realValue:
|
|
value_.real_ = 0.0;
|
|
break;
|
|
|
|
case stringValue:
|
|
value_.string_ = 0;
|
|
break;
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
value_.map_ = new ObjectValues();
|
|
break;
|
|
|
|
case booleanValue:
|
|
value_.bool_ = false;
|
|
break;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::Value(ValueType) : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
|
|
Value::Value(Int value) : type_(intValue)
|
|
{
|
|
value_.int_ = value;
|
|
}
|
|
|
|
Value::Value(UInt value) : type_(uintValue)
|
|
{
|
|
value_.uint_ = value;
|
|
}
|
|
|
|
Value::Value(double value) : type_(realValue)
|
|
{
|
|
value_.real_ = value;
|
|
}
|
|
|
|
Value::Value(char const* value) : type_(stringValue), allocated_(true)
|
|
{
|
|
value_.string_ = valueAllocator()->duplicateStringValue(value);
|
|
}
|
|
|
|
Value::Value(xrpl::Number const& value) : type_(stringValue), allocated_(true)
|
|
{
|
|
auto const tmp = to_string(value);
|
|
value_.string_ = valueAllocator()->duplicateStringValue(tmp.c_str(), tmp.length());
|
|
}
|
|
|
|
Value::Value(std::string const& value) : type_(stringValue), allocated_(true)
|
|
{
|
|
value_.string_ =
|
|
valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length());
|
|
}
|
|
|
|
Value::Value(StaticString const& value) : type_(stringValue)
|
|
{
|
|
value_.string_ = const_cast<char*>(value.c_str());
|
|
}
|
|
|
|
Value::Value(bool value) : type_(booleanValue)
|
|
{
|
|
value_.bool_ = value;
|
|
}
|
|
|
|
Value::Value(Value const& other) : type_(other.type_)
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
case intValue:
|
|
case uintValue:
|
|
case realValue:
|
|
case booleanValue:
|
|
value_ = other.value_;
|
|
break;
|
|
|
|
case stringValue:
|
|
if (other.value_.string_ != nullptr)
|
|
{
|
|
value_.string_ = valueAllocator()->duplicateStringValue(other.value_.string_);
|
|
allocated_ = true;
|
|
}
|
|
else
|
|
{
|
|
value_.string_ = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
value_.map_ = new ObjectValues(*other.value_.map_);
|
|
break;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::Value(Value const&) : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
|
|
Value::~Value()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
case intValue:
|
|
case uintValue:
|
|
case realValue:
|
|
case booleanValue:
|
|
break;
|
|
|
|
case stringValue:
|
|
if (allocated_)
|
|
valueAllocator()->releaseStringValue(value_.string_);
|
|
|
|
break;
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
if (value_.map_ != nullptr)
|
|
delete value_.map_;
|
|
break;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::~Value : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
|
|
Value&
|
|
Value::operator=(Value const& other)
|
|
{
|
|
Value tmp(other);
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
Value::Value(Value&& other) noexcept
|
|
: value_(other.value_), type_(other.type_), allocated_(other.allocated_)
|
|
{
|
|
other.type_ = nullValue;
|
|
other.allocated_ = 0;
|
|
}
|
|
|
|
Value&
|
|
Value::operator=(Value&& other)
|
|
{
|
|
Value tmp(std::move(other));
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
void
|
|
Value::swap(Value& other) noexcept
|
|
{
|
|
std::swap(value_, other.value_);
|
|
|
|
ValueType temp = type_;
|
|
type_ = other.type_;
|
|
other.type_ = temp;
|
|
|
|
int temp2 = allocated_;
|
|
allocated_ = other.allocated_;
|
|
other.allocated_ = temp2;
|
|
}
|
|
|
|
ValueType
|
|
Value::type() const
|
|
{
|
|
return type_;
|
|
}
|
|
|
|
static int
|
|
integerCmp(Int i, UInt ui)
|
|
{
|
|
// All negative numbers are less than all unsigned numbers.
|
|
if (i < 0)
|
|
return -1;
|
|
|
|
// Now we can safely compare.
|
|
if (i < ui)
|
|
return -1;
|
|
return (i == ui) ? 0 : 1;
|
|
}
|
|
|
|
bool
|
|
operator<(Value const& x, Value const& y)
|
|
{
|
|
if (auto signum = x.type_ - y.type_)
|
|
{
|
|
if (x.type_ == intValue && y.type_ == uintValue)
|
|
{
|
|
signum = integerCmp(x.value_.int_, y.value_.uint_);
|
|
}
|
|
else if (x.type_ == uintValue && y.type_ == intValue)
|
|
{
|
|
signum = -integerCmp(y.value_.int_, x.value_.uint_);
|
|
}
|
|
return signum < 0;
|
|
}
|
|
|
|
switch (x.type_)
|
|
{
|
|
case nullValue:
|
|
return false;
|
|
|
|
case intValue:
|
|
return x.value_.int_ < y.value_.int_;
|
|
|
|
case uintValue:
|
|
return x.value_.uint_ < y.value_.uint_;
|
|
|
|
case realValue:
|
|
return x.value_.real_ < y.value_.real_;
|
|
|
|
case booleanValue:
|
|
return static_cast<int>(x.value_.bool_) < static_cast<int>(y.value_.bool_);
|
|
|
|
case stringValue:
|
|
return (x.value_.string_ == 0 && (y.value_.string_ != nullptr)) ||
|
|
((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) &&
|
|
strcmp(x.value_.string_, y.value_.string_) < 0);
|
|
|
|
case arrayValue:
|
|
case objectValue: {
|
|
if (int signum = int(x.value_.map_->size()) - y.value_.map_->size())
|
|
return signum < 0;
|
|
|
|
return *x.value_.map_ < *y.value_.map_;
|
|
}
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::operator<(Value, Value) : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return false; // unreachable
|
|
}
|
|
|
|
bool
|
|
operator==(Value const& x, Value const& y)
|
|
{
|
|
if (x.type_ != y.type_)
|
|
{
|
|
if (x.type_ == intValue && y.type_ == uintValue)
|
|
return integerCmp(x.value_.int_, y.value_.uint_) == 0;
|
|
if (x.type_ == uintValue && y.type_ == intValue)
|
|
return integerCmp(y.value_.int_, x.value_.uint_) == 0;
|
|
return false;
|
|
}
|
|
|
|
switch (x.type_)
|
|
{
|
|
case nullValue:
|
|
return true;
|
|
|
|
case intValue:
|
|
return x.value_.int_ == y.value_.int_;
|
|
|
|
case uintValue:
|
|
return x.value_.uint_ == y.value_.uint_;
|
|
|
|
case realValue:
|
|
return x.value_.real_ == y.value_.real_;
|
|
|
|
case booleanValue:
|
|
return x.value_.bool_ == y.value_.bool_;
|
|
|
|
case stringValue:
|
|
return x.value_.string_ == y.value_.string_ ||
|
|
((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) &&
|
|
(strcmp(x.value_.string_, y.value_.string_) == 0));
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
return x.value_.map_->size() == y.value_.map_->size() &&
|
|
*x.value_.map_ == *y.value_.map_;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::operator==(Value, Value) : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return false; // unreachable
|
|
}
|
|
|
|
char const*
|
|
Value::asCString() const
|
|
{
|
|
XRPL_ASSERT(type_ == stringValue, "Json::Value::asCString : valid type");
|
|
return value_.string_;
|
|
}
|
|
|
|
std::string
|
|
Value::asString() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return "";
|
|
|
|
case stringValue:
|
|
return (value_.string_ != nullptr) ? value_.string_ : "";
|
|
|
|
case booleanValue:
|
|
return value_.bool_ ? "true" : "false";
|
|
|
|
case intValue:
|
|
return std::to_string(value_.int_);
|
|
|
|
case uintValue:
|
|
return std::to_string(value_.uint_);
|
|
|
|
case realValue:
|
|
return std::to_string(value_.real_);
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
JSON_ASSERT_MESSAGE(false, "Type is not convertible to string");
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::asString : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return ""; // unreachable
|
|
}
|
|
|
|
Value::Int
|
|
Value::asInt() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return 0;
|
|
|
|
case intValue:
|
|
return value_.int_;
|
|
|
|
case uintValue:
|
|
JSON_ASSERT_MESSAGE(
|
|
value_.uint_ < (unsigned)maxInt, "integer out of signed integer range");
|
|
return value_.uint_;
|
|
|
|
case realValue:
|
|
JSON_ASSERT_MESSAGE(
|
|
(value_.real_ >= minInt && value_.real_ <= maxInt),
|
|
"Real out of signed integer range");
|
|
return Int(value_.real_);
|
|
|
|
case booleanValue:
|
|
return value_.bool_ ? 1 : 0;
|
|
|
|
case stringValue: {
|
|
char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
|
|
return beast::lexicalCastThrow<int>(str);
|
|
}
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
JSON_ASSERT_MESSAGE(false, "Type is not convertible to int");
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::asInt : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return 0; // unreachable;
|
|
}
|
|
|
|
UInt
|
|
Value::asAbsUInt() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return 0;
|
|
|
|
case intValue: {
|
|
// Doing this conversion through int64 avoids overflow error for
|
|
// value_.int_ = -1 * 2^31 i.e. numeric_limits<int>::min().
|
|
if (value_.int_ < 0)
|
|
return static_cast<std::int64_t>(value_.int_) * -1;
|
|
return value_.int_;
|
|
}
|
|
|
|
case uintValue:
|
|
return value_.uint_;
|
|
|
|
case realValue: {
|
|
if (value_.real_ < 0)
|
|
{
|
|
JSON_ASSERT_MESSAGE(
|
|
-1 * value_.real_ <= maxUInt, "Real out of unsigned integer range");
|
|
return UInt(-1 * value_.real_);
|
|
}
|
|
JSON_ASSERT_MESSAGE(value_.real_ <= maxUInt, "Real out of unsigned integer range");
|
|
return UInt(value_.real_);
|
|
}
|
|
|
|
case booleanValue:
|
|
return value_.bool_ ? 1 : 0;
|
|
|
|
case stringValue: {
|
|
char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
|
|
auto const temp = beast::lexicalCastThrow<std::int64_t>(str);
|
|
if (temp < 0)
|
|
{
|
|
JSON_ASSERT_MESSAGE(-1 * temp <= maxUInt, "String out of unsigned integer range");
|
|
return -1 * temp;
|
|
}
|
|
JSON_ASSERT_MESSAGE(temp <= maxUInt, "String out of unsigned integer range");
|
|
return temp;
|
|
}
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
JSON_ASSERT_MESSAGE(false, "Type is not convertible to int");
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::asAbsInt : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return 0; // unreachable;
|
|
}
|
|
|
|
Value::UInt
|
|
Value::asUInt() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return 0;
|
|
|
|
case intValue:
|
|
JSON_ASSERT_MESSAGE(
|
|
value_.int_ >= 0, "Negative integer can not be converted to unsigned integer");
|
|
return value_.int_;
|
|
|
|
case uintValue:
|
|
return value_.uint_;
|
|
|
|
case realValue:
|
|
JSON_ASSERT_MESSAGE(
|
|
(value_.real_ >= 0 && value_.real_ <= maxUInt),
|
|
"Real out of unsigned integer range");
|
|
return UInt(value_.real_);
|
|
|
|
case booleanValue:
|
|
return value_.bool_ ? 1 : 0;
|
|
|
|
case stringValue: {
|
|
char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
|
|
return beast::lexicalCastThrow<unsigned int>(str);
|
|
}
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
JSON_ASSERT_MESSAGE(false, "Type is not convertible to uint");
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::asUInt : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return 0; // unreachable;
|
|
}
|
|
|
|
double
|
|
Value::asDouble() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return 0.0;
|
|
|
|
case intValue:
|
|
return value_.int_;
|
|
|
|
case uintValue:
|
|
return value_.uint_;
|
|
|
|
case realValue:
|
|
return value_.real_;
|
|
|
|
case booleanValue:
|
|
return value_.bool_ ? 1.0 : 0.0;
|
|
|
|
case stringValue:
|
|
case arrayValue:
|
|
case objectValue:
|
|
JSON_ASSERT_MESSAGE(false, "Type is not convertible to double");
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::asDouble : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return 0; // unreachable;
|
|
}
|
|
|
|
bool
|
|
Value::asBool() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return false;
|
|
|
|
case intValue:
|
|
case uintValue:
|
|
return value_.int_ != 0;
|
|
|
|
case realValue:
|
|
return value_.real_ != 0.0;
|
|
|
|
case booleanValue:
|
|
return value_.bool_;
|
|
|
|
case stringValue:
|
|
return (value_.string_ != nullptr) && value_.string_[0] != 0;
|
|
|
|
case arrayValue:
|
|
case objectValue:
|
|
return !value_.map_->empty();
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::asBool : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return false; // unreachable;
|
|
}
|
|
|
|
bool
|
|
Value::isConvertibleTo(ValueType other) const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case nullValue:
|
|
return true;
|
|
|
|
case intValue:
|
|
return (other == nullValue && value_.int_ == 0) || other == intValue ||
|
|
(other == uintValue && value_.int_ >= 0) || other == realValue ||
|
|
other == stringValue || other == booleanValue;
|
|
|
|
case uintValue:
|
|
return (other == nullValue && value_.uint_ == 0) ||
|
|
(other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue ||
|
|
other == realValue || other == stringValue || other == booleanValue;
|
|
|
|
case realValue:
|
|
return (other == nullValue && value_.real_ == 0.0) ||
|
|
(other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) ||
|
|
(other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt &&
|
|
std::fabs(round(value_.real_) - value_.real_) <
|
|
std::numeric_limits<double>::epsilon()) ||
|
|
other == realValue || other == stringValue || other == booleanValue;
|
|
|
|
case booleanValue:
|
|
return (other == nullValue && !value_.bool_) || other == intValue ||
|
|
other == uintValue || other == realValue || other == stringValue ||
|
|
other == booleanValue;
|
|
|
|
case stringValue:
|
|
return other == stringValue ||
|
|
(other == nullValue && ((value_.string_ == nullptr) || value_.string_[0] == 0));
|
|
|
|
case arrayValue:
|
|
return other == arrayValue || (other == nullValue && value_.map_->empty());
|
|
|
|
case objectValue:
|
|
return other == objectValue || (other == nullValue && value_.map_->empty());
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::isConvertible : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return false; // unreachable;
|
|
}
|
|
|
|
/// Number of values in array or object
|
|
Value::UInt
|
|
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 Int(value_.map_->size());
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("Json::Value::size : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
return 0; // unreachable;
|
|
}
|
|
|
|
Value::
|
|
operator bool() const
|
|
{
|
|
if (isNull())
|
|
return false;
|
|
|
|
if (isString())
|
|
{
|
|
auto s = asCString();
|
|
return (s != nullptr) && (s[0] != 0);
|
|
}
|
|
|
|
return !(isArray() || isObject()) || (size() != 0u);
|
|
}
|
|
|
|
void
|
|
Value::clear()
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == arrayValue || type_ == objectValue,
|
|
"Json::Value::clear : valid type");
|
|
|
|
switch (type_)
|
|
{
|
|
case arrayValue:
|
|
case objectValue:
|
|
value_.map_->clear();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Value&
|
|
Value::operator[](UInt index)
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == arrayValue, "Json::Value::operator[](UInt) : valid type");
|
|
|
|
if (type_ == nullValue)
|
|
*this = Value(arrayValue);
|
|
|
|
CZString key(index);
|
|
ObjectValues::iterator it = value_.map_->lower_bound(key);
|
|
|
|
if (it != value_.map_->end() && (*it).first == key)
|
|
return (*it).second;
|
|
|
|
ObjectValues::value_type defaultValue(key, null);
|
|
it = value_.map_->insert(it, defaultValue);
|
|
return (*it).second;
|
|
}
|
|
|
|
Value const&
|
|
Value::operator[](UInt index) const
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == arrayValue,
|
|
"Json::Value::operator[](UInt) const : valid type");
|
|
|
|
if (type_ == nullValue)
|
|
return null;
|
|
|
|
CZString key(index);
|
|
ObjectValues::const_iterator it = value_.map_->find(key);
|
|
|
|
if (it == value_.map_->end())
|
|
return null;
|
|
|
|
return (*it).second;
|
|
}
|
|
|
|
Value&
|
|
Value::operator[](char const* key)
|
|
{
|
|
return resolveReference(key, false);
|
|
}
|
|
|
|
Value&
|
|
Value::resolveReference(char const* key, bool isStatic)
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == objectValue, "Json::Value::resolveReference : valid type");
|
|
|
|
if (type_ == nullValue)
|
|
*this = Value(objectValue);
|
|
|
|
CZString actualKey(key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy);
|
|
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
|
|
|
|
if (it != value_.map_->end() && (*it).first == actualKey)
|
|
return (*it).second;
|
|
|
|
ObjectValues::value_type defaultValue(actualKey, null);
|
|
it = value_.map_->insert(it, defaultValue);
|
|
Value& value = (*it).second;
|
|
return value;
|
|
}
|
|
|
|
Value
|
|
Value::get(UInt index, Value const& defaultValue) const
|
|
{
|
|
Value const* value = &((*this)[index]);
|
|
return value == &null ? defaultValue : *value;
|
|
}
|
|
|
|
bool
|
|
Value::isValidIndex(UInt index) const
|
|
{
|
|
return index < size();
|
|
}
|
|
|
|
Value const&
|
|
Value::operator[](char const* key) const
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == objectValue,
|
|
"Json::Value::operator[](const char*) const : valid type");
|
|
|
|
if (type_ == nullValue)
|
|
return null;
|
|
|
|
CZString actualKey(key, CZString::noDuplication);
|
|
ObjectValues::const_iterator it = value_.map_->find(actualKey);
|
|
|
|
if (it == value_.map_->end())
|
|
return null;
|
|
|
|
return (*it).second;
|
|
}
|
|
|
|
Value&
|
|
Value::operator[](std::string const& key)
|
|
{
|
|
return (*this)[key.c_str()];
|
|
}
|
|
|
|
Value const&
|
|
Value::operator[](std::string const& key) const
|
|
{
|
|
return (*this)[key.c_str()];
|
|
}
|
|
|
|
Value&
|
|
Value::operator[](StaticString const& key)
|
|
{
|
|
return resolveReference(key, true);
|
|
}
|
|
|
|
Value const&
|
|
Value::operator[](StaticString const& key) const
|
|
{
|
|
return (*this)[key.c_str()];
|
|
}
|
|
|
|
Value&
|
|
Value::append(Value const& value)
|
|
{
|
|
return (*this)[size()] = value;
|
|
}
|
|
|
|
Value&
|
|
Value::append(Value&& value)
|
|
{
|
|
return (*this)[size()] = std::move(value);
|
|
}
|
|
|
|
Value
|
|
Value::get(char const* key, Value const& defaultValue) const
|
|
{
|
|
Value const* value = &((*this)[key]);
|
|
return value == &null ? defaultValue : *value;
|
|
}
|
|
|
|
Value
|
|
Value::get(std::string const& key, Value const& defaultValue) const
|
|
{
|
|
return get(key.c_str(), defaultValue);
|
|
}
|
|
|
|
Value
|
|
Value::removeMember(char const* key)
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == objectValue, "Json::Value::removeMember : valid type");
|
|
|
|
if (type_ == nullValue)
|
|
return null;
|
|
|
|
CZString actualKey(key, CZString::noDuplication);
|
|
ObjectValues::iterator it = value_.map_->find(actualKey);
|
|
|
|
if (it == value_.map_->end())
|
|
return null;
|
|
|
|
Value old(it->second);
|
|
value_.map_->erase(it);
|
|
return old;
|
|
}
|
|
|
|
Value
|
|
Value::removeMember(std::string const& key)
|
|
{
|
|
return removeMember(key.c_str());
|
|
}
|
|
|
|
bool
|
|
Value::isMember(char const* key) const
|
|
{
|
|
if (type_ != objectValue)
|
|
return false;
|
|
|
|
Value const* value = &((*this)[key]);
|
|
return value != &null;
|
|
}
|
|
|
|
bool
|
|
Value::isMember(std::string const& key) const
|
|
{
|
|
return isMember(key.c_str());
|
|
}
|
|
|
|
bool
|
|
Value::isMember(StaticString const& key) const
|
|
{
|
|
return isMember(key.c_str());
|
|
}
|
|
|
|
Value::Members
|
|
Value::getMemberNames() const
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == nullValue || type_ == objectValue, "Json::Value::getMemberNames : valid type");
|
|
|
|
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(std::string((*it).first.c_str()));
|
|
|
|
return members;
|
|
}
|
|
|
|
bool
|
|
Value::isNull() const
|
|
{
|
|
return type_ == nullValue;
|
|
}
|
|
|
|
bool
|
|
Value::isBool() const
|
|
{
|
|
return type_ == booleanValue;
|
|
}
|
|
|
|
bool
|
|
Value::isInt() const
|
|
{
|
|
return type_ == intValue;
|
|
}
|
|
|
|
bool
|
|
Value::isUInt() const
|
|
{
|
|
return type_ == uintValue;
|
|
}
|
|
|
|
bool
|
|
Value::isIntegral() const
|
|
{
|
|
return type_ == intValue || type_ == uintValue || type_ == booleanValue;
|
|
}
|
|
|
|
bool
|
|
Value::isDouble() const
|
|
{
|
|
return type_ == realValue;
|
|
}
|
|
|
|
bool
|
|
Value::isNumeric() const
|
|
{
|
|
return isIntegral() || isDouble();
|
|
}
|
|
|
|
bool
|
|
Value::isString() const
|
|
{
|
|
return type_ == stringValue;
|
|
}
|
|
|
|
bool
|
|
Value::isArray() const
|
|
{
|
|
return type_ == arrayValue;
|
|
}
|
|
|
|
bool
|
|
Value::isArrayOrNull() const
|
|
{
|
|
return type_ == nullValue || type_ == arrayValue;
|
|
}
|
|
|
|
bool
|
|
Value::isObject() const
|
|
{
|
|
return type_ == objectValue;
|
|
}
|
|
|
|
bool
|
|
Value::isObjectOrNull() const
|
|
{
|
|
return type_ == nullValue || type_ == objectValue;
|
|
}
|
|
|
|
std::string
|
|
Value::toStyledString() const
|
|
{
|
|
StyledWriter writer;
|
|
return writer.write(*this);
|
|
}
|
|
|
|
Value::const_iterator
|
|
Value::begin() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case arrayValue:
|
|
case objectValue:
|
|
if (value_.map_ != nullptr)
|
|
return const_iterator(value_.map_->begin());
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return const_iterator();
|
|
}
|
|
|
|
Value::const_iterator
|
|
Value::end() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case arrayValue:
|
|
case objectValue:
|
|
if (value_.map_ != nullptr)
|
|
return const_iterator(value_.map_->end());
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return const_iterator();
|
|
}
|
|
|
|
Value::iterator
|
|
Value::begin()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case arrayValue:
|
|
case objectValue:
|
|
if (value_.map_ != nullptr)
|
|
return iterator(value_.map_->begin());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return iterator();
|
|
}
|
|
|
|
Value::iterator
|
|
Value::end()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case arrayValue:
|
|
case objectValue:
|
|
if (value_.map_ != nullptr)
|
|
return iterator(value_.map_->end());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return iterator();
|
|
}
|
|
|
|
} // namespace Json
|