mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-06 23:00:20 +00:00
1230 lines
30 KiB
C++
1230 lines
30 KiB
C++
#include <xrpl/json/json_value.h>
|
|
|
|
#include <xrpl/basics/Number.h>
|
|
#include <xrpl/basics/contract.h> // IWYU pragma: keep
|
|
#include <xrpl/beast/core/LexicalCast.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/json/detail/json_assert.h>
|
|
#include <xrpl/json/json_errors.h> // IWYU pragma: keep
|
|
#include <xrpl/json/json_forwards.h>
|
|
#include <xrpl/json/json_writer.h>
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
namespace json {
|
|
|
|
Value const Value::kNull;
|
|
|
|
class DefaultValueAllocator : public ValueAllocator
|
|
{
|
|
public:
|
|
~DefaultValueAllocator() override = 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 = kUnknown) override
|
|
{
|
|
//@todo investigate this old optimization
|
|
// if ( !value || value[0] == 0 )
|
|
// return 0;
|
|
|
|
if (length == kUnknown)
|
|
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* kValueAllocator = new DefaultValueAllocator; // NOLINT TODO
|
|
return kValueAllocator;
|
|
}
|
|
|
|
static struct DummyValueAllocatorInitializer
|
|
{
|
|
DummyValueAllocatorInitializer()
|
|
{
|
|
valueAllocator(); // ensure valueAllocator() statics are initialized
|
|
// before main().
|
|
}
|
|
} gDummyValueAllocatorInitializer;
|
|
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
// 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 == DuplicationPolicy::Duplicate ? valueAllocator()->makeMemberName(cstr) : cstr)
|
|
, index_(static_cast<int>(allocate))
|
|
{
|
|
}
|
|
|
|
Value::CZString::CZString(CZString const& other)
|
|
: cstr_(
|
|
other.index_ != static_cast<int>(DuplicationPolicy::NoDuplication) && other.cstr_ != 0
|
|
? valueAllocator()->makeMemberName(other.cstr_)
|
|
: other.cstr_)
|
|
, index_([&]() -> int {
|
|
if (!other.cstr_)
|
|
return other.index_;
|
|
return other.index_ == static_cast<int>(DuplicationPolicy::NoDuplication)
|
|
? static_cast<int>(DuplicationPolicy::NoDuplication)
|
|
: static_cast<int>(DuplicationPolicy::Duplicate);
|
|
}())
|
|
{
|
|
}
|
|
|
|
Value::CZString::~CZString()
|
|
{
|
|
if ((cstr_ != nullptr) && index_ == static_cast<int>(DuplicationPolicy::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::cStr() const
|
|
{
|
|
return cstr_;
|
|
}
|
|
|
|
bool
|
|
Value::CZString::isStaticString() const
|
|
{
|
|
return index_ == static_cast<int>(DuplicationPolicy::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 ValueType::Null:
|
|
break;
|
|
|
|
case ValueType::Int:
|
|
case ValueType::UInt:
|
|
value_.intVal = 0;
|
|
break;
|
|
|
|
case ValueType::Real:
|
|
value_.realVal = 0.0;
|
|
break;
|
|
|
|
case ValueType::String:
|
|
value_.stringVal = 0;
|
|
break;
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
value_.mapVal = new ObjectValues();
|
|
break;
|
|
|
|
case ValueType::Boolean:
|
|
value_.boolVal = false;
|
|
break;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("json::Value::Value(ValueType) : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
|
|
Value::Value(Int value) : type_(ValueType::Int)
|
|
{
|
|
value_.intVal = value;
|
|
}
|
|
|
|
Value::Value(UInt value) : type_(ValueType::UInt)
|
|
{
|
|
value_.uintVal = value;
|
|
}
|
|
|
|
Value::Value(double value) : type_(ValueType::Real)
|
|
{
|
|
value_.realVal = value;
|
|
}
|
|
|
|
Value::Value(char const* value) : type_(ValueType::String), allocated_(true)
|
|
{
|
|
value_.stringVal = valueAllocator()->duplicateStringValue(value);
|
|
}
|
|
|
|
Value::Value(xrpl::Number const& value) : type_(ValueType::String), allocated_(true)
|
|
{
|
|
auto const tmp = to_string(value);
|
|
value_.stringVal = valueAllocator()->duplicateStringValue(tmp.c_str(), tmp.length());
|
|
}
|
|
|
|
Value::Value(std::string const& value) : type_(ValueType::String), allocated_(true)
|
|
{
|
|
value_.stringVal =
|
|
valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length());
|
|
}
|
|
|
|
Value::Value(StaticString const& value) : type_(ValueType::String)
|
|
{
|
|
value_.stringVal = const_cast<char*>(value.cStr());
|
|
}
|
|
|
|
Value::Value(bool value) : type_(ValueType::Boolean)
|
|
{
|
|
value_.boolVal = value;
|
|
}
|
|
|
|
Value::Value(Value const& other) : type_(other.type_)
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Null:
|
|
case ValueType::Int:
|
|
case ValueType::UInt:
|
|
case ValueType::Real:
|
|
case ValueType::Boolean:
|
|
value_ = other.value_;
|
|
break;
|
|
|
|
case ValueType::String:
|
|
if (other.value_.stringVal != nullptr)
|
|
{
|
|
value_.stringVal = valueAllocator()->duplicateStringValue(other.value_.stringVal);
|
|
allocated_ = true;
|
|
}
|
|
else
|
|
{
|
|
value_.stringVal = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
value_.mapVal = new ObjectValues(*other.value_.mapVal);
|
|
break;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("json::Value::Value(Value const&) : invalid type");
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
|
|
Value::~Value()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Null:
|
|
case ValueType::Int:
|
|
case ValueType::UInt:
|
|
case ValueType::Real:
|
|
case ValueType::Boolean:
|
|
break;
|
|
|
|
case ValueType::String:
|
|
if (allocated_)
|
|
valueAllocator()->releaseStringValue(value_.stringVal);
|
|
|
|
break;
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
if (value_.mapVal != nullptr)
|
|
delete value_.mapVal;
|
|
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_ = ValueType::Null;
|
|
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 const temp = type_;
|
|
type_ = other.type_;
|
|
other.type_ = temp;
|
|
|
|
int const 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 = static_cast<int>(x.type_) - static_cast<int>(y.type_))
|
|
{
|
|
if (x.type_ == ValueType::Int && y.type_ == ValueType::UInt)
|
|
{
|
|
signum = integerCmp(x.value_.intVal, y.value_.uintVal);
|
|
}
|
|
else if (x.type_ == ValueType::UInt && y.type_ == ValueType::Int)
|
|
{
|
|
signum = -integerCmp(y.value_.intVal, x.value_.uintVal);
|
|
}
|
|
return signum < 0;
|
|
}
|
|
|
|
switch (x.type_)
|
|
{
|
|
case ValueType::Null:
|
|
return false;
|
|
|
|
case ValueType::Int:
|
|
return x.value_.intVal < y.value_.intVal;
|
|
|
|
case ValueType::UInt:
|
|
return x.value_.uintVal < y.value_.uintVal;
|
|
|
|
case ValueType::Real:
|
|
return x.value_.realVal < y.value_.realVal;
|
|
|
|
case ValueType::Boolean:
|
|
return static_cast<int>(x.value_.boolVal) < static_cast<int>(y.value_.boolVal);
|
|
|
|
case ValueType::String:
|
|
return (x.value_.stringVal == 0 && (y.value_.stringVal != nullptr)) ||
|
|
((y.value_.stringVal != nullptr) && (x.value_.stringVal != nullptr) &&
|
|
strcmp(x.value_.stringVal, y.value_.stringVal) < 0);
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object: {
|
|
if (int const signum = int(x.value_.mapVal->size()) - y.value_.mapVal->size())
|
|
return signum < 0;
|
|
|
|
return *x.value_.mapVal < *y.value_.mapVal;
|
|
}
|
|
|
|
// 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_ == ValueType::Int && y.type_ == ValueType::UInt)
|
|
return integerCmp(x.value_.intVal, y.value_.uintVal) == 0;
|
|
if (x.type_ == ValueType::UInt && y.type_ == ValueType::Int)
|
|
return integerCmp(y.value_.intVal, x.value_.uintVal) == 0;
|
|
return false;
|
|
}
|
|
|
|
switch (x.type_)
|
|
{
|
|
case ValueType::Null:
|
|
return true;
|
|
|
|
case ValueType::Int:
|
|
return x.value_.intVal == y.value_.intVal;
|
|
|
|
case ValueType::UInt:
|
|
return x.value_.uintVal == y.value_.uintVal;
|
|
|
|
case ValueType::Real:
|
|
return x.value_.realVal == y.value_.realVal;
|
|
|
|
case ValueType::Boolean:
|
|
return x.value_.boolVal == y.value_.boolVal;
|
|
|
|
case ValueType::String:
|
|
return x.value_.stringVal == y.value_.stringVal ||
|
|
((y.value_.stringVal != nullptr) && (x.value_.stringVal != nullptr) &&
|
|
(strcmp(x.value_.stringVal, y.value_.stringVal) == 0));
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
return x.value_.mapVal->size() == y.value_.mapVal->size() &&
|
|
*x.value_.mapVal == *y.value_.mapVal;
|
|
|
|
// 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_ == ValueType::String, "json::Value::asCString : valid type");
|
|
return value_.stringVal;
|
|
}
|
|
|
|
std::string
|
|
Value::asString() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Null:
|
|
return "";
|
|
|
|
case ValueType::String:
|
|
return (value_.stringVal != nullptr) ? value_.stringVal : "";
|
|
|
|
case ValueType::Boolean:
|
|
return value_.boolVal ? "true" : "false";
|
|
|
|
case ValueType::Int:
|
|
return std::to_string(value_.intVal);
|
|
|
|
case ValueType::UInt:
|
|
return std::to_string(value_.uintVal);
|
|
|
|
case ValueType::Real:
|
|
return std::to_string(value_.realVal);
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
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 ValueType::Null:
|
|
return 0;
|
|
|
|
case ValueType::Int:
|
|
return value_.intVal;
|
|
|
|
case ValueType::UInt:
|
|
JSON_ASSERT_MESSAGE(
|
|
value_.uintVal < (unsigned)kMaxInt, "integer out of signed integer range");
|
|
return value_.uintVal;
|
|
|
|
case ValueType::Real:
|
|
JSON_ASSERT_MESSAGE(
|
|
(value_.realVal >= kMinInt && value_.realVal <= kMaxInt),
|
|
"Real out of signed integer range");
|
|
return Int(value_.realVal);
|
|
|
|
case ValueType::Boolean:
|
|
return value_.boolVal ? 1 : 0;
|
|
|
|
case ValueType::String: {
|
|
char const* const str{(value_.stringVal != nullptr) ? value_.stringVal : ""};
|
|
return beast::lexicalCastThrow<int>(str);
|
|
}
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
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 ValueType::Null:
|
|
return 0;
|
|
|
|
case ValueType::Int: {
|
|
// Doing this conversion through int64 avoids overflow error for
|
|
// value_.intVal = -1 * 2^31 i.e. numeric_limits<int>::min().
|
|
if (value_.intVal < 0)
|
|
return static_cast<std::int64_t>(value_.intVal) * -1;
|
|
return value_.intVal;
|
|
}
|
|
|
|
case ValueType::UInt:
|
|
return value_.uintVal;
|
|
|
|
case ValueType::Real: {
|
|
if (value_.realVal < 0)
|
|
{
|
|
JSON_ASSERT_MESSAGE(
|
|
-1 * value_.realVal <= kMaxUInt, "Real out of unsigned integer range");
|
|
return UInt(-1 * value_.realVal);
|
|
}
|
|
JSON_ASSERT_MESSAGE(value_.realVal <= kMaxUInt, "Real out of unsigned integer range");
|
|
return UInt(value_.realVal);
|
|
}
|
|
|
|
case ValueType::Boolean:
|
|
return value_.boolVal ? 1 : 0;
|
|
|
|
case ValueType::String: {
|
|
char const* const str{(value_.stringVal != nullptr) ? value_.stringVal : ""};
|
|
auto const temp = beast::lexicalCastThrow<std::int64_t>(str);
|
|
if (temp < 0)
|
|
{
|
|
JSON_ASSERT_MESSAGE(-1 * temp <= kMaxUInt, "String out of unsigned integer range");
|
|
return -1 * temp;
|
|
}
|
|
JSON_ASSERT_MESSAGE(temp <= kMaxUInt, "String out of unsigned integer range");
|
|
return temp;
|
|
}
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
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 ValueType::Null:
|
|
return 0;
|
|
|
|
case ValueType::Int:
|
|
JSON_ASSERT_MESSAGE(
|
|
value_.intVal >= 0, "Negative integer can not be converted to unsigned integer");
|
|
return value_.intVal;
|
|
|
|
case ValueType::UInt:
|
|
return value_.uintVal;
|
|
|
|
case ValueType::Real:
|
|
JSON_ASSERT_MESSAGE(
|
|
(value_.realVal >= 0 && value_.realVal <= kMaxUInt),
|
|
"Real out of unsigned integer range");
|
|
return UInt(value_.realVal);
|
|
|
|
case ValueType::Boolean:
|
|
return value_.boolVal ? 1 : 0;
|
|
|
|
case ValueType::String: {
|
|
char const* const str{(value_.stringVal != nullptr) ? value_.stringVal : ""};
|
|
return beast::lexicalCastThrow<unsigned int>(str);
|
|
}
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
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 ValueType::Null:
|
|
return 0.0;
|
|
|
|
case ValueType::Int:
|
|
return value_.intVal;
|
|
|
|
case ValueType::UInt:
|
|
return value_.uintVal;
|
|
|
|
case ValueType::Real:
|
|
return value_.realVal;
|
|
|
|
case ValueType::Boolean:
|
|
return value_.boolVal ? 1.0 : 0.0;
|
|
|
|
case ValueType::String:
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
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 ValueType::Null:
|
|
return false;
|
|
|
|
case ValueType::Int:
|
|
case ValueType::UInt:
|
|
return value_.intVal != 0;
|
|
|
|
case ValueType::Real:
|
|
return value_.realVal != 0.0;
|
|
|
|
case ValueType::Boolean:
|
|
return value_.boolVal;
|
|
|
|
case ValueType::String:
|
|
return (value_.stringVal != nullptr) && value_.stringVal[0] != 0;
|
|
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
return !value_.mapVal->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 ValueType::Null:
|
|
return true;
|
|
|
|
case ValueType::Int:
|
|
return (other == ValueType::Null && value_.intVal == 0) || other == ValueType::Int ||
|
|
(other == ValueType::UInt && value_.intVal >= 0) || other == ValueType::Real ||
|
|
other == ValueType::String || other == ValueType::Boolean;
|
|
|
|
case ValueType::UInt:
|
|
return (other == ValueType::Null && value_.uintVal == 0) ||
|
|
(other == ValueType::Int && value_.uintVal <= (unsigned)kMaxInt) ||
|
|
other == ValueType::UInt || other == ValueType::Real ||
|
|
other == ValueType::String || other == ValueType::Boolean;
|
|
|
|
case ValueType::Real:
|
|
return (other == ValueType::Null && value_.realVal == 0.0) ||
|
|
(other == ValueType::Int && value_.realVal >= kMinInt &&
|
|
value_.realVal <= kMaxInt) ||
|
|
(other == ValueType::UInt && value_.realVal >= 0 && value_.realVal <= kMaxUInt &&
|
|
std::fabs(round(value_.realVal) - value_.realVal) <
|
|
std::numeric_limits<double>::epsilon()) ||
|
|
other == ValueType::Real || other == ValueType::String ||
|
|
other == ValueType::Boolean;
|
|
|
|
case ValueType::Boolean:
|
|
return (other == ValueType::Null && !value_.boolVal) || other == ValueType::Int ||
|
|
other == ValueType::UInt || other == ValueType::Real ||
|
|
other == ValueType::String || other == ValueType::Boolean;
|
|
|
|
case ValueType::String:
|
|
return other == ValueType::String ||
|
|
(other == ValueType::Null &&
|
|
((value_.stringVal == nullptr) || value_.stringVal[0] == 0));
|
|
|
|
case ValueType::Array:
|
|
return other == ValueType::Array ||
|
|
(other == ValueType::Null && value_.mapVal->empty());
|
|
|
|
case ValueType::Object:
|
|
return other == ValueType::Object ||
|
|
(other == ValueType::Null && value_.mapVal->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 ValueType::Null:
|
|
case ValueType::Int:
|
|
case ValueType::UInt:
|
|
case ValueType::Real:
|
|
case ValueType::Boolean:
|
|
case ValueType::String:
|
|
return 0;
|
|
|
|
case ValueType::Array: // size of the array is highest index + 1
|
|
if (!value_.mapVal->empty())
|
|
{
|
|
auto itLast = value_.mapVal->end();
|
|
--itLast;
|
|
return (*itLast).first.index() + 1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
case ValueType::Object:
|
|
return Int(value_.mapVal->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_ == ValueType::Null || type_ == ValueType::Array || type_ == ValueType::Object,
|
|
"json::Value::clear : valid type");
|
|
|
|
switch (type_)
|
|
{
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
value_.mapVal->clear();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Value&
|
|
Value::operator[](UInt index)
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == ValueType::Null || type_ == ValueType::Array,
|
|
"json::Value::operator[](UInt) : valid type");
|
|
|
|
if (type_ == ValueType::Null)
|
|
*this = Value(ValueType::Array);
|
|
|
|
CZString const key(index);
|
|
auto it = value_.mapVal->lower_bound(key);
|
|
|
|
if (it != value_.mapVal->end() && (*it).first == key)
|
|
return (*it).second;
|
|
|
|
ObjectValues::value_type const defaultValue(key, kNull);
|
|
it = value_.mapVal->insert(it, defaultValue);
|
|
return (*it).second;
|
|
}
|
|
|
|
Value const&
|
|
Value::operator[](UInt index) const
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == ValueType::Null || type_ == ValueType::Array,
|
|
"json::Value::operator[](UInt) const : valid type");
|
|
|
|
if (type_ == ValueType::Null)
|
|
return kNull;
|
|
|
|
CZString const key(index);
|
|
auto const it = value_.mapVal->find(key);
|
|
|
|
if (it == value_.mapVal->end())
|
|
return kNull;
|
|
|
|
return (*it).second;
|
|
}
|
|
|
|
Value&
|
|
Value::operator[](char const* key)
|
|
{
|
|
return resolveReference(key, false);
|
|
}
|
|
|
|
Value&
|
|
Value::resolveReference(char const* key, bool isStatic)
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == ValueType::Null || type_ == ValueType::Object,
|
|
"json::Value::resolveReference : valid type");
|
|
|
|
if (type_ == ValueType::Null)
|
|
*this = Value(ValueType::Object);
|
|
|
|
CZString const actualKey(
|
|
key,
|
|
isStatic ? CZString::DuplicationPolicy::NoDuplication
|
|
: CZString::DuplicationPolicy::DuplicateOnCopy);
|
|
auto it = value_.mapVal->lower_bound(actualKey);
|
|
|
|
if (it != value_.mapVal->end() && (*it).first == actualKey)
|
|
return (*it).second;
|
|
|
|
ObjectValues::value_type const defaultValue(actualKey, kNull);
|
|
it = value_.mapVal->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 == &kNull ? defaultValue : *value;
|
|
}
|
|
|
|
bool
|
|
Value::isValidIndex(UInt index) const
|
|
{
|
|
return index < size();
|
|
}
|
|
|
|
Value const&
|
|
Value::operator[](char const* key) const
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == ValueType::Null || type_ == ValueType::Object,
|
|
"json::Value::operator[](const char*) const : valid type");
|
|
|
|
if (type_ == ValueType::Null)
|
|
return kNull;
|
|
|
|
CZString const actualKey(key, CZString::DuplicationPolicy::NoDuplication);
|
|
auto const it = value_.mapVal->find(actualKey);
|
|
|
|
if (it == value_.mapVal->end())
|
|
return kNull;
|
|
|
|
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.cStr()];
|
|
}
|
|
|
|
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 == &kNull ? 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_ == ValueType::Null || type_ == ValueType::Object,
|
|
"json::Value::removeMember : valid type");
|
|
|
|
if (type_ == ValueType::Null)
|
|
return kNull;
|
|
|
|
CZString const actualKey(key, CZString::DuplicationPolicy::NoDuplication);
|
|
auto const it = value_.mapVal->find(actualKey);
|
|
|
|
if (it == value_.mapVal->end())
|
|
return kNull;
|
|
|
|
Value old(it->second);
|
|
value_.mapVal->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_ != ValueType::Object)
|
|
return false;
|
|
|
|
Value const* value = &((*this)[key]);
|
|
return value != &kNull;
|
|
}
|
|
|
|
bool
|
|
Value::isMember(std::string const& key) const
|
|
{
|
|
return isMember(key.c_str());
|
|
}
|
|
|
|
bool
|
|
Value::isMember(StaticString const& key) const
|
|
{
|
|
return isMember(key.cStr());
|
|
}
|
|
|
|
Value::Members
|
|
Value::getMemberNames() const
|
|
{
|
|
XRPL_ASSERT(
|
|
type_ == ValueType::Null || type_ == ValueType::Object,
|
|
"json::Value::getMemberNames : valid type");
|
|
|
|
if (type_ == ValueType::Null)
|
|
return Value::Members();
|
|
|
|
Members members;
|
|
members.reserve(value_.mapVal->size());
|
|
auto it = value_.mapVal->begin();
|
|
auto const itEnd = value_.mapVal->end();
|
|
|
|
for (; it != itEnd; ++it)
|
|
members.emplace_back((*it).first.cStr());
|
|
|
|
return members;
|
|
}
|
|
|
|
bool
|
|
Value::isNull() const
|
|
{
|
|
return type_ == ValueType::Null;
|
|
}
|
|
|
|
bool
|
|
Value::isBool() const
|
|
{
|
|
return type_ == ValueType::Boolean;
|
|
}
|
|
|
|
bool
|
|
Value::isInt() const
|
|
{
|
|
return type_ == ValueType::Int;
|
|
}
|
|
|
|
bool
|
|
Value::isUInt() const
|
|
{
|
|
return type_ == ValueType::UInt;
|
|
}
|
|
|
|
bool
|
|
Value::isIntegral() const
|
|
{
|
|
return type_ == ValueType::Int || type_ == ValueType::UInt || type_ == ValueType::Boolean;
|
|
}
|
|
|
|
bool
|
|
Value::isDouble() const
|
|
{
|
|
return type_ == ValueType::Real;
|
|
}
|
|
|
|
bool
|
|
Value::isNumeric() const
|
|
{
|
|
return isIntegral() || isDouble();
|
|
}
|
|
|
|
bool
|
|
Value::isString() const
|
|
{
|
|
return type_ == ValueType::String;
|
|
}
|
|
|
|
bool
|
|
Value::isArray() const
|
|
{
|
|
return type_ == ValueType::Array;
|
|
}
|
|
|
|
bool
|
|
Value::isArrayOrNull() const
|
|
{
|
|
return type_ == ValueType::Null || type_ == ValueType::Array;
|
|
}
|
|
|
|
bool
|
|
Value::isObject() const
|
|
{
|
|
return type_ == ValueType::Object;
|
|
}
|
|
|
|
bool
|
|
Value::isObjectOrNull() const
|
|
{
|
|
return type_ == ValueType::Null || type_ == ValueType::Object;
|
|
}
|
|
|
|
std::string
|
|
Value::toStyledString() const
|
|
{
|
|
StyledWriter writer;
|
|
return writer.write(*this);
|
|
}
|
|
|
|
Value::const_iterator
|
|
Value::begin() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
if (value_.mapVal != nullptr)
|
|
return const_iterator(value_.mapVal->begin());
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return const_iterator();
|
|
}
|
|
|
|
Value::const_iterator
|
|
Value::end() const
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
if (value_.mapVal != nullptr)
|
|
return const_iterator(value_.mapVal->end());
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return const_iterator();
|
|
}
|
|
|
|
Value::iterator
|
|
Value::begin()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
if (value_.mapVal != nullptr)
|
|
return iterator(value_.mapVal->begin());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return iterator();
|
|
}
|
|
|
|
Value::iterator
|
|
Value::end()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case ValueType::Array:
|
|
case ValueType::Object:
|
|
if (value_.mapVal != nullptr)
|
|
return iterator(value_.mapVal->end());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return iterator();
|
|
}
|
|
|
|
} // namespace json
|