Files
rippled/include/xrpl/json/json_value.h
2026-05-15 15:32:19 +00:00

680 lines
16 KiB
C++

#pragma once
#include <xrpl/basics/Number.h>
#include <xrpl/json/json_forwards.h>
#include <cstring>
#include <limits>
#include <map>
#include <string>
#include <vector>
/** \brief JSON (JavaScript Object Notation).
*/
namespace json {
/** \brief Type of the value held by a Value object.
*/
enum class ValueType {
Null = 0, ///< 'null' value
Int, ///< signed integer value
UInt, ///< unsigned integer value
Real, ///< double value
String, ///< UTF-8 string value
Boolean, ///< bool value
Array, ///< array value (ordered list)
Object ///< object value (collection of name/value pairs).
};
/** \brief Lightweight wrapper to tag static string.
*
* Value constructor and ValueType::Object member assignment takes advantage of the
* StaticString and avoid the cost of string duplication when storing the
* string or the member name.
*
* Example of usage:
* \code
* json::Value aValue( StaticString("some text") );
* json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
class StaticString
{
public:
constexpr explicit StaticString(char const* czString) : str_(czString)
{
}
constexpr
operator char const*() const
{
return str_;
}
[[nodiscard]] constexpr char const*
cStr() const
{
return str_;
}
private:
char const* str_;
};
inline bool
operator==(StaticString x, StaticString y)
{
return strcmp(x.cStr(), y.cStr()) == 0;
}
inline bool
operator!=(StaticString x, StaticString y)
{
return !(x == y);
}
inline bool
operator==(std::string const& x, StaticString y)
{
return strcmp(x.c_str(), y.cStr()) == 0;
}
inline bool
operator!=(std::string const& x, StaticString y)
{
return !(x == y);
}
inline bool
operator==(StaticString x, std::string const& y)
{
return y == x;
}
inline bool
operator!=(StaticString x, std::string const& y)
{
return !(y == x);
}
/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
*
* This class is a discriminated union wrapper that can represent a:
* - signed integer [range: Value::kMinInt - Value::kMaxInt]
* - unsigned integer (range: 0 - Value::kMaxUInt)
* - double
* - UTF-8 string
* - boolean
* - 'null'
* - an ordered list of Value
* - collection of name/value pairs (javascript object)
*
* The type of the held value is represented by a #ValueType and
* can be obtained using type().
*
* values of an ValueType::Object or ValueType::Array can be accessed using operator[]()
* methods. Non const methods will automatically create the a ValueType::Null element
* if it does not exist.
* The sequence of an ValueType::Array will be automatically resize and initialized
* with ValueType::Null. resize() can be used to enlarge or truncate an ValueType::Array.
*
* The get() methods can be used to obtain a default value in the case the
* required element does not exist.
*
* It is possible to iterate over the list of a ValueType::Object values using
* the getMemberNames() method.
*/
class Value
{
friend class ValueIteratorBase;
public:
using Members = std::vector<std::string>;
using iterator = ValueIterator;
using const_iterator = ValueConstIterator;
using UInt = json::UInt;
using Int = json::Int;
using ArrayIndex = UInt;
static Value const kNull;
static constexpr Int kMinInt = std::numeric_limits<Int>::min();
static constexpr Int kMaxInt = std::numeric_limits<Int>::max();
static constexpr UInt kMaxUInt = std::numeric_limits<UInt>::max();
private:
class CZString
{
public:
enum class DuplicationPolicy { NoDuplication = 0, Duplicate, DuplicateOnCopy };
CZString(int index);
CZString(char const* cstr, DuplicationPolicy allocate);
CZString(CZString const& other);
~CZString();
CZString&
operator=(CZString const& other) = delete;
bool
operator<(CZString const& other) const;
bool
operator==(CZString const& other) const;
[[nodiscard]] int
index() const;
[[nodiscard]] char const*
cStr() const;
[[nodiscard]] bool
isStaticString() const;
private:
char const* cstr_;
int index_;
};
public:
using ObjectValues = std::map<CZString, Value>;
public:
/** \brief Create a default Value of the given type.
This is a very useful constructor.
To create an empty array, pass ValueType::Array.
To create an empty object, pass ValueType::Object.
Another Value can then be set to this one by assignment.
This is useful since clear() and resize() will not alter types.
Examples:
\code
json::Value null_value; // null
json::Value arr_value(json::ValueType::Array); // []
json::Value obj_value(json::ValueType::Object); // {}
\endcode
*/
Value(ValueType type = ValueType::Null);
Value(Int value);
Value(UInt value);
Value(double value);
Value(char const* value);
Value(xrpl::Number const& value);
/** \brief Constructs a value from a static string.
* Like other value string constructor but do not duplicate the string for
* internal storage. The given string must remain alive after the call to
this
* constructor.
* Example of usage:
* \code
* json::Value aValue( StaticString("some text") );
* \endcode
*/
Value(StaticString const& value);
Value(std::string const& value);
Value(bool value);
Value(Value const& other);
~Value();
Value&
operator=(Value const& other);
Value&
operator=(Value&& other);
Value(Value&& other) noexcept;
/// Swap values.
void
swap(Value& other) noexcept;
[[nodiscard]] ValueType
type() const;
[[nodiscard]] char const*
asCString() const;
/** Returns the unquoted string value. */
[[nodiscard]] std::string
asString() const;
[[nodiscard]] Int
asInt() const;
[[nodiscard]] UInt
asUInt() const;
[[nodiscard]] double
asDouble() const;
[[nodiscard]] bool
asBool() const;
/** Correct absolute value from int or unsigned int */
[[nodiscard]] UInt
asAbsUInt() const;
// TODO: What is the "empty()" method this docstring mentions?
/** isNull() tests to see if this field is null. Don't use this method to
test for emptiness: use empty(). */
[[nodiscard]] bool
isNull() const;
[[nodiscard]] bool
isBool() const;
[[nodiscard]] bool
isInt() const;
[[nodiscard]] bool
isUInt() const;
[[nodiscard]] bool
isIntegral() const;
[[nodiscard]] bool
isDouble() const;
[[nodiscard]] bool
isNumeric() const;
[[nodiscard]] bool
isString() const;
[[nodiscard]] bool
isArray() const;
[[nodiscard]] bool
isArrayOrNull() const;
[[nodiscard]] bool
isObject() const;
[[nodiscard]] bool
isObjectOrNull() const;
[[nodiscard]] bool
isConvertibleTo(ValueType other) const;
/// Number of values in array or object
[[nodiscard]] UInt
size() const;
/** Returns false if this is an empty array, empty object, empty string,
or null. */
explicit
operator bool() const;
/// Remove all object members and array elements.
/// \pre type() is ValueType::Array, ValueType::Object, or ValueType::Null
/// \post type() is unchanged
void
clear();
/// Access an array element (zero based index ).
/// If the array contains less than index element, then null value are
/// inserted in the array so that its size is index+1. (You may need to say
/// 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value&
operator[](UInt index);
/// Access an array element (zero based index )
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value const&
operator[](UInt index) const;
/// If the array contains at least index+1 elements, returns the element
/// value, otherwise returns defaultValue.
[[nodiscard]] Value
get(UInt index, Value const& defaultValue) const;
/// Return true if index < size().
[[nodiscard]] bool
isValidIndex(UInt index) const;
/// \brief Append value to array at the end.
///
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
Value&
append(Value const& value);
Value&
append(Value&& value);
/// Access an object value by name, create a null member if it does not
/// exist.
Value&
operator[](char const* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
Value const&
operator[](char const* key) const;
/// Access an object value by name, create a null member if it does not
/// exist.
Value&
operator[](std::string const& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
Value const&
operator[](std::string const& key) const;
/** \brief Access an object value by name, create a null member if it does
not exist.
* If the object as no entry for that name, then the member name used to
store
* the new entry is not duplicated.
* Example of use:
* \code
* json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
Value&
operator[](StaticString const& key);
Value const&
operator[](StaticString const& key) const;
/// Return the member named key if it exist, defaultValue otherwise.
Value
get(char const* key, Value const& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
[[nodiscard]] Value
get(std::string const& key, Value const& defaultValue) const;
/// \brief Remove and return the named member.
///
/// Do nothing if it did not exist.
/// \return the removed Value, or null.
/// \pre type() is ValueType::Object or ValueType::Null
/// \post type() is unchanged
Value
removeMember(char const* key);
/// Same as removeMember(const char*)
Value
removeMember(std::string const& key);
/// Return true if the object has a member named key.
bool
isMember(char const* key) const;
/// Return true if the object has a member named key.
[[nodiscard]] bool
isMember(std::string const& key) const;
/// Return true if the object has a member named key.
[[nodiscard]] bool
isMember(StaticString const& key) const;
/// \brief Return a list of the member names.
///
/// If null, return an empty list.
/// \pre type() is ValueType::Object or ValueType::Null
/// \post if type() was ValueType::Null, it remains ValueType::Null
[[nodiscard]] Members
getMemberNames() const;
[[nodiscard]] std::string
toStyledString() const;
[[nodiscard]] const_iterator
begin() const;
[[nodiscard]] const_iterator
end() const;
iterator
begin();
iterator
end();
friend bool
operator==(Value const&, Value const&);
friend bool
operator<(Value const&, Value const&);
private:
Value&
resolveReference(char const* key, bool isStatic);
private:
union ValueHolder
{
Int intVal;
UInt uintVal;
double realVal;
bool boolVal;
char* stringVal;
ObjectValues* mapVal{nullptr};
} value_;
ValueType type_ : 8;
int allocated_ : 1 {}; // Notes: if declared as bool, bitfield is useless.
};
inline Value
toJson(xrpl::Number const& number)
{
return to_string(number);
}
bool
operator==(Value const&, Value const&);
inline bool
operator!=(Value const& x, Value const& y)
{
return !(x == y);
}
bool
operator<(Value const&, Value const&);
inline bool
operator<=(Value const& x, Value const& y)
{
return !(y < x);
}
inline bool
operator>(Value const& x, Value const& y)
{
return y < x;
}
inline bool
operator>=(Value const& x, Value const& y)
{
return !(x < y);
}
/** \brief Experimental do not use: Allocator to customize member name and
* string value memory management done by Value.
*
* - makeMemberName() and releaseMemberName() are called to respectively
* duplicate and free an json::ValueType::Object member name.
* - duplicateStringValue() and releaseStringValue() are called similarly to
* duplicate and free a json::ValueType::String value.
*/
class ValueAllocator
{
public:
static constexpr auto kUnknown = (unsigned)-1;
virtual ~ValueAllocator() = default;
virtual char*
makeMemberName(char const* memberName) = 0;
virtual void
releaseMemberName(char* memberName) = 0;
virtual char*
duplicateStringValue(char const* value, unsigned int length = kUnknown) = 0;
virtual void
releaseStringValue(char* value) = 0;
};
/** \brief base class for Value iterators.
*
*/
class ValueIteratorBase
{
public:
using size_t = unsigned int;
using difference_type = int;
using SelfType = ValueIteratorBase;
ValueIteratorBase();
explicit ValueIteratorBase(Value::ObjectValues::iterator const& current);
bool
operator==(SelfType const& other) const
{
return isEqual(other);
}
bool
operator!=(SelfType const& other) const
{
return !isEqual(other);
}
/// Return either the index or the member name of the referenced value as a
/// Value.
[[nodiscard]] Value
key() const;
/// Return the index of the referenced Value. -1 if it is not an ValueType::Array.
[[nodiscard]] UInt
index() const;
/// Return the member name of the referenced Value. "" if it is not an
/// ValueType::Object.
[[nodiscard]] char const*
memberName() const;
protected:
[[nodiscard]] Value&
deref() const;
void
increment();
void
decrement();
[[nodiscard]] difference_type
computeDistance(SelfType const& other) const;
[[nodiscard]] bool
isEqual(SelfType const& other) const;
void
copy(SelfType const& other);
private:
Value::ObjectValues::iterator current_;
// Indicates that iterator is for a null value.
bool isNull_;
};
/** \brief const iterator for object and array value.
*
*/
class ValueConstIterator : public ValueIteratorBase
{
friend class Value;
public:
using size_t = unsigned int;
using difference_type = int;
using reference = Value const&;
using pointer = Value const*;
using SelfType = ValueConstIterator;
ValueConstIterator() = default;
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueConstIterator(Value::ObjectValues::iterator const& current);
public:
SelfType&
operator=(ValueIteratorBase const& other);
SelfType
operator++(int)
{
SelfType temp(*this);
++*this;
return temp;
}
SelfType
operator--(int)
{
SelfType temp(*this);
--*this;
return temp;
}
SelfType&
operator--()
{
decrement();
return *this;
}
SelfType&
operator++()
{
increment();
return *this;
}
reference
operator*() const
{
return deref();
}
};
/** \brief Iterator for object and array value.
*/
class ValueIterator : public ValueIteratorBase
{
friend class Value;
public:
using size_t = unsigned int;
using difference_type = int;
using reference = Value&;
using pointer = Value*;
using SelfType = ValueIterator;
ValueIterator() = default;
ValueIterator(ValueConstIterator const& other);
ValueIterator(ValueIterator const& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueIterator(Value::ObjectValues::iterator const& current);
public:
SelfType&
operator=(SelfType const& other);
SelfType
operator++(int)
{
SelfType const temp(*this);
++*this;
return temp;
}
SelfType
operator--(int)
{
SelfType const temp(*this);
--*this;
return temp;
}
SelfType&
operator--()
{
decrement();
return *this;
}
SelfType&
operator++()
{
increment();
return *this;
}
reference
operator*() const
{
return deref();
}
};
} // namespace json