Get that strong Bips field type working (mostly)

This commit is contained in:
Ed Hennis
2025-04-24 15:16:31 -04:00
parent a5376c7d76
commit c3fef1b9ad
8 changed files with 251 additions and 169 deletions

View File

@@ -49,13 +49,14 @@ template <int>
class STBitString;
template <class>
class STInteger;
template <class>
class STTypedInteger;
class STNumber;
class STXChainBridge;
class STVector256;
class STCurrency;
template <class Integer>
concept HasValue = requires(Integer i) { i.value(); };
#pragma push_macro("XMACRO")
#undef XMACRO
@@ -366,8 +367,8 @@ using SF_UINT512 = TypedField<STBitString<512>>;
// The tag is only applied when deserialized.
//
// Tenth of a basis point values:
using SF_TENTHBIPS16 = TypedField<STTypedInteger<TenthBips16>>;
using SF_TENTHBIPS32 = TypedField<STTypedInteger<TenthBips32>>;
using SF_TENTHBIPS16 = TypedField<STInteger<TenthBips16>>;
using SF_TENTHBIPS32 = TypedField<STInteger<TenthBips32>>;
using SF_ACCOUNT = TypedField<STAccount>;
using SF_AMOUNT = TypedField<STAmount>;

View File

@@ -62,22 +62,22 @@ struct STExchange<STInteger<U>, T>
};
template <class U, class T>
struct STExchange<STTypedInteger<U>, T>
struct STExchange<STInteger<TenthBips<U>>, T>
{
explicit STExchange() = default;
using value_type = U;
using value_type = TenthBips<U>;
static void
get(std::optional<T>& t, STTypedInteger<U> const& u)
get(std::optional<T>& t, STInteger<value_type> const& u)
{
t = u.value();
}
static std::unique_ptr<STInteger<U>>
static std::unique_ptr<STInteger<value_type>>
set(SField const& f, T const& t)
{
return std::make_unique<STTypedInteger<U>>(f, t);
return std::make_unique<STInteger<value_type>>(f, t);
}
};

View File

@@ -37,7 +37,7 @@ private:
public:
explicit STInteger(Integer v);
STInteger(SField const& n, Integer v = 0);
STInteger(SField const& n, Integer v = {});
STInteger(SerialIter& sit, SField const& name);
SerializedTypeID
@@ -82,42 +82,9 @@ using STUInt16 = STInteger<std::uint16_t>;
using STUInt32 = STInteger<std::uint32_t>;
using STUInt64 = STInteger<std::uint64_t>;
template <class TypedInteger>
class STTypedInteger : public STInteger<typename TypedInteger::value_type>,
public CountedObject<STTypedInteger<TypedInteger>>
{
public:
using value_type = TypedInteger;
using base_value_type = TypedInteger::value_type;
using base = STInteger<base_value_type>;
public:
using base::add;
using base::getJson;
using base::getSType;
using base::getText;
using base::isDefault;
using base::isEquivalent;
using base::STInteger;
SerializedTypeID
getSType() const override;
STTypedInteger&
operator=(value_type const& v);
value_type
value() const noexcept;
void
setValue(TypedInteger v);
operator TypedInteger() const;
};
// Tenth of a basis point values:
using STTenthBips16 = STTypedInteger<TenthBips16>;
using STTenthBips32 = STTypedInteger<TenthBips32>;
using STTenthBips16 = STInteger<TenthBips16>;
using STTenthBips32 = STInteger<TenthBips32>;
template <typename Integer>
inline STInteger<Integer>::STInteger(Integer v) : value_(v)
@@ -199,34 +166,6 @@ inline STInteger<Integer>::operator Integer() const
return value_;
}
template <typename TypedInteger>
inline STTypedInteger<TypedInteger>&
STTypedInteger<TypedInteger>::operator=(value_type const& v)
{
base::setValue(v.value());
return *this;
}
template <typename TypedInteger>
inline typename STTypedInteger<TypedInteger>::value_type
STTypedInteger<TypedInteger>::value() const noexcept
{
return value_type(base::value());
}
template <typename TypedInteger>
inline void
STTypedInteger<TypedInteger>::setValue(TypedInteger v)
{
base::setValue(v.value());
}
template <typename TypedInteger>
inline STTypedInteger<TypedInteger>::operator TypedInteger() const
{
return TypedInteger(base::value());
}
} // namespace ripple
#endif

View File

@@ -125,8 +125,17 @@ public:
}
template <typename Integer>
requires(!HasValue<Integer>)
int addInteger(Integer);
template <typename Integer>
requires HasValue<Integer>
int
addInteger(Integer i)
{
return addInteger(i.value());
}
template <std::size_t Bits, class Tag>
int
addBitString(base_uint<Bits, Tag> const& v)

View File

@@ -592,6 +592,17 @@ safe_cast(Src s) noexcept
return Dest{safe_cast<typename Dest::value_type>(s.value())};
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_integral_v<typename Dest::value_type> &&
std::is_integral_v<typename Src>,
Dest>
safe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{safe_cast<typename Dest::value_type>(s)};
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
@@ -604,6 +615,17 @@ unsafe_cast(Src s) noexcept
return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_integral_v<typename Dest::value_type> &&
std::is_integral_v<typename Src>,
Dest>
unsafe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{unsafe_cast<typename Dest::value_type>(s)};
}
} // namespace ripple
#endif // PROTOCOL_UNITS_H_INCLUDED

View File

@@ -229,18 +229,62 @@ STUInt64::getJson(JsonOptions) const
return convertToString(value_, 16); // Convert to base 16
}
//------------------------------------------------------------------------------
template <>
STTenthBips16::STInteger(SerialIter& sit, SField const& name)
: STInteger(name, TenthBips16(sit.get16()))
{
}
template <>
SerializedTypeID
STTenthBips16::getSType() const override
STTenthBips16::getSType() const
{
return STI_TENTHBIPS16;
}
template <>
std::string
STTenthBips16::getText() const
{
return std::to_string(value_.value());
}
template <>
Json::Value
STTenthBips16::getJson(JsonOptions) const
{
return value_.value();
}
//------------------------------------------------------------------------------
template <>
STTenthBips32::STInteger(SerialIter& sit, SField const& name)
: STInteger(name, TenthBips32(sit.get32()))
{
}
template <>
SerializedTypeID
STTenthBips32::getSType() const override
STTenthBips32::getSType() const
{
return STI_TENTHBIPS32;
}
template <>
std::string
STTenthBips32::getText() const
{
return std::to_string(value_.value());
}
template <>
Json::Value
STTenthBips32::getJson(JsonOptions) const
{
return value_.value();
}
} // namespace ripple

View File

@@ -82,6 +82,31 @@ constexpr std::
return static_cast<U1>(value);
}
template <typename U, typename S>
requires HasValue<U>
constexpr std::enable_if_t<
std::is_unsigned<typename U::value_type>::value && std::is_signed<S>::value,
U>
to_unsigned(S value)
{
if (value < 0 || std::numeric_limits<typename U::value_type>::max() < value)
Throw<std::runtime_error>("Value out of range");
return safe_cast<U>(static_cast<typename U::value_type>(value));
}
template <typename U1, typename U2>
requires HasValue<U1>
constexpr std::enable_if_t<
std::is_unsigned<typename U1::value_type>::value &&
std::is_unsigned<U2>::value,
U1>
to_unsigned(U2 value)
{
if (std::numeric_limits<typename U1::value_type>::max() < value)
Throw<std::runtime_error>("Value out of range");
return safe_cast<U1>(static_cast<typename U1::value_type>(value));
}
static std::string
make_name(std::string const& object, std::string const& field)
{
@@ -201,6 +226,119 @@ non_object_in_array(std::string const& item, Json::UInt index)
" is not an object. Arrays may only contain objects.");
}
template <class STResult, class Integer>
static std::optional<detail::STVar>
parseUnsigned(
SField const& field,
std::string const& json_name,
std::string const& fieldName,
SField const* name,
Json::Value const& value,
Json::Value& error)
{
std::optional<detail::STVar> ret;
try
{
if (value.isString())
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
beast::lexicalCastThrow<Integer>(value.asString())));
}
else if (value.isInt())
{
ret = detail::make_stvar<STResult>(
field,
to_unsigned<typename STResult::value_type>(value.asInt()));
}
else if (value.isUInt())
{
ret = detail::make_stvar<STResult>(
field,
to_unsigned<typename STResult::value_type>(value.asUInt()));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
return ret;
}
return ret;
}
template <class STResult>
static std::optional<detail::STVar>
parseUint16(
SField const& field,
std::string const& json_name,
std::string const& fieldName,
SField const* name,
Json::Value const& value,
Json::Value& error)
{
std::optional<detail::STVar> ret;
try
{
if (value.isString())
{
std::string const strValue = value.asString();
if (!strValue.empty() &&
((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionType)
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
static_cast<std::uint16_t>(
TxFormats::getInstance().findTypeByName(
strValue))));
if (*name == sfGeneric)
name = &sfTransaction;
}
else if (field == sfLedgerEntryType)
{
ret = detail::make_stvar<STResult>(
field,
safe_cast<typename STResult::value_type>(
static_cast<std::uint16_t>(
LedgerFormats::getInstance().findTypeByName(
strValue))));
if (*name == sfGeneric)
name = &sfLedgerEntry;
}
else
{
error = invalid_data(json_name, fieldName);
return ret;
}
}
}
if (!ret)
return parseUnsigned<STResult, std::uint16_t>(
field, json_name, fieldName, name, value, error);
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
return ret;
}
return ret;
}
// This function is used by parseObject to parse any JSON type that doesn't
// recurse. Everything represented here is a leaf-type.
static std::optional<detail::STVar>
@@ -301,105 +439,34 @@ parseLeaf(
break;
case STI_UINT16:
try
{
if (value.isString())
{
std::string const strValue = value.asString();
if (!strValue.empty() &&
((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionType)
{
ret = detail::make_stvar<STUInt16>(
field,
static_cast<std::uint16_t>(
TxFormats::getInstance().findTypeByName(
strValue)));
if (*name == sfGeneric)
name = &sfTransaction;
}
else if (field == sfLedgerEntryType)
{
ret = detail::make_stvar<STUInt16>(
field,
static_cast<std::uint16_t>(
LedgerFormats::getInstance().findTypeByName(
strValue)));
if (*name == sfGeneric)
name = &sfLedgerEntry;
}
else
{
error = invalid_data(json_name, fieldName);
return ret;
}
}
else
{
ret = detail::make_stvar<STUInt16>(
field,
beast::lexicalCastThrow<std::uint16_t>(strValue));
}
}
else if (value.isInt())
{
ret = detail::make_stvar<STUInt16>(
field, to_unsigned<std::uint16_t>(value.asInt()));
}
else if (value.isUInt())
{
ret = detail::make_stvar<STUInt16>(
field, to_unsigned<std::uint16_t>(value.asUInt()));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
ret = parseUint16<STUInt16>(
field, json_name, fieldName, name, value, error);
if (!ret)
return ret;
break;
case STI_TENTHBIPS16:
ret = parseUnsigned<STTenthBips16, std::uint16_t>(
field, json_name, fieldName, name, value, error);
if (!ret)
return ret;
}
break;
case STI_UINT32:
try
{
if (value.isString())
{
ret = detail::make_stvar<STUInt32>(
field,
beast::lexicalCastThrow<std::uint32_t>(
value.asString()));
}
else if (value.isInt())
{
ret = detail::make_stvar<STUInt32>(
field, to_unsigned<std::uint32_t>(value.asInt()));
}
else if (value.isUInt())
{
ret = detail::make_stvar<STUInt32>(
field, safe_cast<std::uint32_t>(value.asUInt()));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
ret = parseUnsigned<STUInt32, std::uint32_t>(
field, json_name, fieldName, name, value, error);
if (!ret)
return ret;
break;
case STI_TENTHBIPS32:
ret = parseUnsigned<STTenthBips32, std::uint32_t>(
field, json_name, fieldName, name, value, error);
if (!ret)
return ret;
}
break;

View File

@@ -805,7 +805,7 @@ class Simulate_test : public beast::unit_test::suite
testTx(env, tx, testSimulation);
tx[sfSigningPubKey] = "";
tx[sfTxnSignature] = "x";
tx[sfTxnSignature] = "";
tx[sfSequence] = env.seq(env.master);
tx[sfFee] = env.current()->fees().base.jsonClipped().asString();