mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
* Fix: Don't flag consensus as stalled prematurely (#5658) Fix stalled consensus detection to prevent false positives in situations where there are no disputed transactions. Stalled consensus detection was added to 2.5.0 in response to a network consensus halt that caused a round to run for over an hour. However, it has a flaw that makes it very easy to have false positives. Those false positives are usually mitigated by other checks that prevent them from having an effect, but there have been several instances of validators "running ahead" because there are circumstances where the other checks are "successful", allowing the stall state to be checked. * Set version to 2.5.1 * fix: Skip processing transaction batch if the batch is empty (#5670) Avoids an assertion failure in NetworkOPsImp::apply in the unlikely event that all incoming transactions are invalid. * Fix: EscrowTokenV1 (#5571) * resolves an accounting inconsistency in MPT escrows where transfer fees were not properly handled when unlocking escrowed tokens. * refactor: Wrap GitHub CI conditionals in curly braces (#5796) This change wraps all GitHub conditionals in `${{ .. }}`, both for consistency and to reduce unexpected failures, because it was previously noticed that not all conditionals work without those curly braces. * Only notify clio for PRs targeting the release and master branches (#5794) Clio should only be notified when releases are about to be made, instead of for all PR, so this change only notifies Clio when a PR targets the release or master branch. * Support DynamicMPT XLS-94d (#5705) * extends the functionality of the MPTokenIssuanceSet transaction, allowing the issuer to update fields or flags that were explicitly marked as mutable during creation. * Bugfix: Adds graceful peer disconnection (#5669) The XRPL establishes connections in three stages: first a TCP connection, then a TLS/SSL handshake to secure the connection, and finally an upgrade to the bespoke XRP Ledger peer-to-peer protocol. During connection termination, xrpld directly closes the TCP connection, bypassing the TLS/SSL shutdown handshake. This makes peer disconnection diagnostics more difficult - abrupt TCP termination appears as if the peer crashed rather than disconnected gracefully. This change refactors the connection lifecycle with the following changes: - Enhanced outgoing connection logic with granular timeouts for each connection stage (TCP, TLS, XRPL handshake) to improve diagnostic capabilities - Updated both PeerImp and ConnectAttempt to use proper asynchronous TLS shutdown procedures for graceful connection termination * Downgrade to boost 1.83 * Set version to 2.6.1-rc1 * chore: Use self hosted windows runners (#5780) This changes switches from the GitHub-managed Windows runners to self-hosted runners to significantly reduce build time. * Rename mutable flags (#5797) This is a minor change on top of #5705 * fix(amendment): Add missing fields for keylets to ledger objects (#5646) This change adds a fix amendment (`fixIncludeKeyletFields`) that adds: * `sfSequence` to `Escrow` and `PayChannel` * `sfOwner` to `SignerList` * `sfOracleDocumentID` to `Oracle` This ensures that all ledger entries hold all the information needed to determine their keylet. * chore: Limits CI build and test parallelism to reduce resource contention (#5799) GitHub runners have a limit on how many concurrent jobs they can actually process (even though they will try to run them all at the same time), and similarly the Conan remote cannot handle hundreds of concurrent requests. Previously, the Conan dependency uploading was already limited to max 10 jobs running in parallel, and this change makes the same change to the build+test workflow. * chore: Build and test all configs for daily scheduled run (#5801) This change re-enables building and testing all configurations, but only for the daily scheduled run. Previously all configurations were run for each merge into the develop branch, but that overwhelmed both the GitHub runners and the Conan remote, and thus they were limited to just a subset of configurations. Now that the number of jobs is limited via `max-parallel: 10`, we should be able to safely enable building all configurations again. However, building them all once a day instead of for each PR merge should be sufficient. * chore: Add unit tests dir to code coverage excludes (#5803) This change excludes unit test code from code coverage reporting. * refactor: Modularise ledger (#5493) This change moves the ledger code to libxrpl. * Mark PermissionDelegation as unsupported * Set version to 2.6.1-rc2 * Miscellaneous refactors and updates (#5590) - Added a new Invariant: `ValidPseudoAccounts` which checks that all pseudo-accounts behave consistently through creation and updates, and that no "real" accounts look like pseudo-accounts (which means they don't have a 0 sequence). - `to_short_string(base_uint)`. Like `to_string`, but only returns the first 8 characters. (Similar to how a git commit ID can be abbreviated.) Used as a wrapped sink to prefix most transaction-related messages. More can be added later. - `XRPL_ASSERT_PARTS`. Convenience wrapper for `XRPL_ASSERT`, which takes the `function` and `description` as separate parameters. - `SField::sMD_PseudoAccount`. Metadata option for `SField` definitions to indicate that the field, if set in an `AccountRoot` indicates that account is a pseudo-account. Removes the need for hard-coded field lists all over the place. Added the flag to `AMMID` and `VaultID`. - Added functionality to `SField` ctor to detect both code and name collisions using asserts. And require all SFields to have a name - Convenience type aliases `STLedgerEntry::const_pointer` and `STLedgerEntry::const_ref`. (`SLE` is an alias to `STLedgerEntry`.) - Generalized `feeunit.h` (`TaggedFee`) into `unit.h` (`ValueUnit`) and added new "BIPS"-related tags for future use. Also refactored the type restrictions to use Concepts. - Restructured `transactions.macro` to do two big things 1. Include the `#include` directives for transactor header files directly in the macro file. Removes the need to update `applySteps.cpp` and the resulting conflicts. 2. Added a `privileges` parameter to the `TRANSACTION` macro, which specifies some of the operations a transaction is allowed to do. These `privileges` are enforced by invariant checks. Again, removed the need to update scattered lists of transaction types in various checks. - Unit tests: 1. Moved more helper functions into `TestHelpers.h` and `.cpp`. 2. Cleaned up the namespaces to prevent / mitigate random collisions and ambiguous symbols, particularly in unity builds. 3. Generalized `Env::balance` to add support for `MPTIssue` and `Asset`. 4. Added a set of helper classes to simplify `Env` transaction parameter classes: `JTxField`, `JTxFieldWrapper`, and a bunch of classes derived or aliased from it. For an example of how awesome it is, check the changes `src/test/jtx/escrow.h` for how much simpler the definitions are for `finish_time`, `cancel_time`, `condition`, and `fulfillment`. 5. Generalized several of the amount-related helper classes to understand `Asset`s. 6. `env.balance` for an MPT issuer will return a negative number (or 0) for consistency with IOUs. * refactor: Simplify STParsedJSON with some helper functions (#5591) - Add code coverage for STParsedJSON edge cases Co-authored-by: Denis Angell <dangell@transia.co> * test: Add STInteger and STParsedJSON tests (#5726) This change is to improve code coverage (and to simplify #5720 and #5725); there is otherwise no change in functionality. The change adds basic tests for `STInteger` and `STParsedJSON`, so it becomes easier to test smaller changes to the types, as well as removes `STParsedJSONArray`, since it is not used anywhere (including in Clio). * Revert "Update Conan dependencies: OpenSSL" (#5807) This change reverts #5617, because it will require extensive testing that will take up more time than we have before the next scheduled release. Reverting this change does not mean we are abandoning it. We aim to pick it back up once there's a sufficient time window to allow for testing on multiple distros running a mixture of OpenSSL 1.x and 3.x. * docs: Add warning about using std::counting_semaphore (#5595) This adds a comment to avoid using `std::counting_semaphore` until the minimum compiler versions of GCC and Clang have been updated to no longer contain the bug that is present in older compilers. * Improve ValidatorList invalid UNL manifest logging (#5804) This change raises logging severity from `INFO` to `WARN` when handling UNL manifest signed with an unexpected / invalid key. It also changes the internal error code for an invalid format of UNL manifest to `invalid` (from `untrusted`). This is a follow up to problems experienced by an UNL node due to old manifest key configured in `validators.txt`, which would be easier to diagnose with improved logging. It also replaces a log line with `UNREACHABLE` for an impossible situation when we match UNL manifest key against a configured key which has an invalid type (we cannot configure such a key because of checks when loading configured keys). * chore: Pin all CI Docker tags (#5813) To avoid surprises and ensure reproducibility, this change pins all CI Docker image tags to the latest version in the XRPLF/CI repo. * change `fixPriceOracleOrder` to `Supported::yes` (#5749) * fix: Address http header case sensitivity (#5767) This change makes the regex in `HttpClient.cpp` that matches the content-length http header case insensitive to improve compatibility, as http headers are case insensitive. * test: add more comprehensive tests for `FeeVote` (#5746) This change adds more comprehensive tests for the `FeeVote` module, which previously only checked the basics, and not the more comprehensive flows in that class. * ci: Call all reusable workflows reusable (#5818) * Add `STInt32` as a new `SType` (#5788) This change adds `STInt32` as a new `SType` under the `STInteger` umbrella, with `SType` value `12`. This is the first and only `STInteger` type that supports negative values. * switch `fixIncludeKeyletFields` to `Supported::yes` (#5819) * refactor: Restructure Transactor::preflight to reduce boilerplate (#5592) * Restructures `Transactor::preflight` to create several functions that will remove the need for error-prone boilerplate code in derived classes' implementations of `preflight`. * refactor: Add support for extra transaction signatures (#5594) * Restructures Transactor signature checking code to be able to handle a `sigObject`, which may be the full transaction, or may be an object field containing a separate signature. Either way, the `sigObject` can be a single- or multi-sign signature. * ci: Upload artifacts during build and test in a separate job (#5817) * chore: Set free-form CI inputs as env vars (#5822) This change moves CI values that could be user-provided into environment variables. * Rename flags for DynamicMPT (#5820) * Set version to 2.6.1 * fix: FD/handle guarding + exponential backoff (#5823) * fix: Transaction sig checking functions do not get a full context (#5829) Fixes a (currently harmless) bug introduced by PR #5594 * Remove bogus coverage warning (#5838) * fix return type --------- Co-authored-by: Ed Hennis <ed@ripple.com> Co-authored-by: Jingchen <a1q123456@users.noreply.github.com> Co-authored-by: Denis Angell <dangell@transia.co> Co-authored-by: Bart <bthomee@users.noreply.github.com> Co-authored-by: yinyiqian1 <yqian@ripple.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Co-authored-by: Bronek Kozicki <brok@incorrekt.com> Co-authored-by: Mayukha Vadari <mvadari@ripple.com> Co-authored-by: Valentin Balaschenko <13349202+vlntb@users.noreply.github.com> Co-authored-by: tequ <git@tequ.dev> Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
1257 lines
30 KiB
C++
1257 lines
30 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef RIPPLE_PROTOCOL_STOBJECT_H_INCLUDED
|
|
#define RIPPLE_PROTOCOL_STOBJECT_H_INCLUDED
|
|
|
|
#include <xrpl/basics/CountedObject.h>
|
|
#include <xrpl/basics/Slice.h>
|
|
#include <xrpl/basics/chrono.h>
|
|
#include <xrpl/basics/contract.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/protocol/HashPrefix.h>
|
|
#include <xrpl/protocol/SOTemplate.h>
|
|
#include <xrpl/protocol/STAmount.h>
|
|
#include <xrpl/protocol/STBase.h>
|
|
#include <xrpl/protocol/STCurrency.h>
|
|
#include <xrpl/protocol/STIssue.h>
|
|
#include <xrpl/protocol/STPathSet.h>
|
|
#include <xrpl/protocol/STVector256.h>
|
|
#include <xrpl/protocol/Units.h>
|
|
#include <xrpl/protocol/detail/STVar.h>
|
|
|
|
#include <boost/iterator/transform_iterator.hpp>
|
|
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace ripple {
|
|
|
|
class STArray;
|
|
|
|
inline void
|
|
throwFieldNotFound(SField const& field)
|
|
{
|
|
Throw<std::runtime_error>("Field not found: " + field.getName());
|
|
}
|
|
|
|
class STObject : public STBase, public CountedObject<STObject>
|
|
{
|
|
// Proxy value for a STBase derived class
|
|
template <class T>
|
|
class Proxy;
|
|
template <class T>
|
|
class ValueProxy;
|
|
template <class T>
|
|
class OptionalProxy;
|
|
|
|
struct Transform
|
|
{
|
|
explicit Transform() = default;
|
|
|
|
using argument_type = detail::STVar;
|
|
using result_type = STBase;
|
|
|
|
STBase const&
|
|
operator()(detail::STVar const& e) const;
|
|
};
|
|
|
|
using list_type = std::vector<detail::STVar>;
|
|
|
|
list_type v_;
|
|
SOTemplate const* mType;
|
|
|
|
public:
|
|
using iterator = boost::
|
|
transform_iterator<Transform, STObject::list_type::const_iterator>;
|
|
|
|
virtual ~STObject() = default;
|
|
STObject(STObject const&) = default;
|
|
|
|
template <typename F>
|
|
STObject(SOTemplate const& type, SField const& name, F&& f)
|
|
: STObject(type, name)
|
|
{
|
|
f(*this);
|
|
}
|
|
|
|
STObject&
|
|
operator=(STObject const&) = default;
|
|
STObject(STObject&&);
|
|
STObject&
|
|
operator=(STObject&& other);
|
|
|
|
STObject(SOTemplate const& type, SField const& name);
|
|
STObject(SOTemplate const& type, SerialIter& sit, SField const& name);
|
|
STObject(SerialIter& sit, SField const& name, int depth = 0);
|
|
STObject(SerialIter&& sit, SField const& name);
|
|
explicit STObject(SField const& name);
|
|
|
|
static STObject
|
|
makeInnerObject(SField const& name);
|
|
|
|
iterator
|
|
begin() const;
|
|
|
|
iterator
|
|
end() const;
|
|
|
|
bool
|
|
empty() const;
|
|
|
|
void
|
|
reserve(std::size_t n);
|
|
|
|
void
|
|
applyTemplate(SOTemplate const& type);
|
|
|
|
void
|
|
applyTemplateFromSField(SField const&);
|
|
|
|
bool
|
|
isFree() const;
|
|
|
|
void
|
|
set(SOTemplate const&);
|
|
|
|
bool
|
|
set(SerialIter& u, int depth = 0);
|
|
|
|
SerializedTypeID
|
|
getSType() const override;
|
|
|
|
bool
|
|
isEquivalent(STBase const& t) const override;
|
|
|
|
bool
|
|
isDefault() const override;
|
|
|
|
void
|
|
add(Serializer& s) const override;
|
|
|
|
std::string
|
|
getFullText() const override;
|
|
|
|
std::string
|
|
getText() const override;
|
|
|
|
// TODO(tom): options should be an enum.
|
|
Json::Value getJson(JsonOptions = JsonOptions::none) const override;
|
|
|
|
void
|
|
addWithoutSigningFields(Serializer& s) const;
|
|
|
|
Serializer
|
|
getSerializer() const;
|
|
|
|
template <class... Args>
|
|
std::size_t
|
|
emplace_back(Args&&... args);
|
|
|
|
int
|
|
getCount() const;
|
|
|
|
bool setFlag(std::uint32_t);
|
|
bool clearFlag(std::uint32_t);
|
|
bool isFlag(std::uint32_t) const;
|
|
|
|
std::uint32_t
|
|
getFlags() const;
|
|
|
|
uint256
|
|
getHash(HashPrefix prefix) const;
|
|
|
|
uint256
|
|
getSigningHash(HashPrefix prefix) const;
|
|
|
|
STBase const&
|
|
peekAtIndex(int offset) const;
|
|
|
|
STBase&
|
|
getIndex(int offset);
|
|
|
|
STBase const*
|
|
peekAtPIndex(int offset) const;
|
|
|
|
STBase*
|
|
getPIndex(int offset);
|
|
|
|
int
|
|
getFieldIndex(SField const& field) const;
|
|
|
|
SField const&
|
|
getFieldSType(int index) const;
|
|
|
|
STBase const&
|
|
peekAtField(SField const& field) const;
|
|
|
|
STBase&
|
|
getField(SField const& field);
|
|
|
|
STBase const*
|
|
peekAtPField(SField const& field) const;
|
|
|
|
STBase*
|
|
getPField(SField const& field, bool createOkay = false);
|
|
|
|
// these throw if the field type doesn't match, or return default values
|
|
// if the field is optional but not present
|
|
unsigned char
|
|
getFieldU8(SField const& field) const;
|
|
std::uint16_t
|
|
getFieldU16(SField const& field) const;
|
|
std::uint32_t
|
|
getFieldU32(SField const& field) const;
|
|
std::uint64_t
|
|
getFieldU64(SField const& field) const;
|
|
uint128
|
|
getFieldH128(SField const& field) const;
|
|
|
|
uint160
|
|
getFieldH160(SField const& field) const;
|
|
uint192
|
|
getFieldH192(SField const& field) const;
|
|
uint256
|
|
getFieldH256(SField const& field) const;
|
|
std::int32_t
|
|
getFieldI32(SField const& field) const;
|
|
AccountID
|
|
getAccountID(SField const& field) const;
|
|
|
|
Blob
|
|
getFieldVL(SField const& field) const;
|
|
STAmount const&
|
|
getFieldAmount(SField const& field) const;
|
|
STPathSet const&
|
|
getFieldPathSet(SField const& field) const;
|
|
STVector256 const&
|
|
getFieldV256(SField const& field) const;
|
|
STArray const&
|
|
getFieldArray(SField const& field) const;
|
|
STCurrency const&
|
|
getFieldCurrency(SField const& field) const;
|
|
STNumber const&
|
|
getFieldNumber(SField const& field) const;
|
|
|
|
/** Get the value of a field.
|
|
@param A TypedField built from an SField value representing the desired
|
|
object field. In typical use, the TypedField will be implicitly
|
|
constructed.
|
|
@return The value of the specified field.
|
|
@throws STObject::FieldErr if the field is not present.
|
|
*/
|
|
template <class T>
|
|
typename T::value_type
|
|
operator[](TypedField<T> const& f) const;
|
|
|
|
/** Get the value of a field as a std::optional
|
|
|
|
@param An OptionaledField built from an SField value representing the
|
|
desired object field. In typical use, the OptionaledField will be
|
|
constructed by using the ~ operator on an SField.
|
|
@return std::nullopt if the field is not present, else the value of
|
|
the specified field.
|
|
*/
|
|
template <class T>
|
|
std::optional<std::decay_t<typename T::value_type>>
|
|
operator[](OptionaledField<T> const& of) const;
|
|
|
|
/** Get a modifiable field value.
|
|
@param A TypedField built from an SField value representing the desired
|
|
object field. In typical use, the TypedField will be implicitly
|
|
constructed.
|
|
@return A modifiable reference to the value of the specified field.
|
|
@throws STObject::FieldErr if the field is not present.
|
|
*/
|
|
template <class T>
|
|
ValueProxy<T>
|
|
operator[](TypedField<T> const& f);
|
|
|
|
/** Return a modifiable field value as std::optional
|
|
|
|
@param An OptionaledField built from an SField value representing the
|
|
desired object field. In typical use, the OptionaledField will be
|
|
constructed by using the ~ operator on an SField.
|
|
@return Transparent proxy object to an `optional` holding a modifiable
|
|
reference to the value of the specified field. Returns
|
|
std::nullopt if the field is not present.
|
|
*/
|
|
template <class T>
|
|
OptionalProxy<T>
|
|
operator[](OptionaledField<T> const& of);
|
|
|
|
/** Get the value of a field.
|
|
@param A TypedField built from an SField value representing the desired
|
|
object field. In typical use, the TypedField will be implicitly
|
|
constructed.
|
|
@return The value of the specified field.
|
|
@throws STObject::FieldErr if the field is not present.
|
|
*/
|
|
template <class T>
|
|
typename T::value_type
|
|
at(TypedField<T> const& f) const;
|
|
|
|
/** Get the value of a field as std::optional
|
|
|
|
@param An OptionaledField built from an SField value representing the
|
|
desired object field. In typical use, the OptionaledField will be
|
|
constructed by using the ~ operator on an SField.
|
|
@return std::nullopt if the field is not present, else the value of
|
|
the specified field.
|
|
*/
|
|
template <class T>
|
|
std::optional<std::decay_t<typename T::value_type>>
|
|
at(OptionaledField<T> const& of) const;
|
|
|
|
/** Get a modifiable field value.
|
|
@param A TypedField built from an SField value representing the desired
|
|
object field. In typical use, the TypedField will be implicitly
|
|
constructed.
|
|
@return A modifiable reference to the value of the specified field.
|
|
@throws STObject::FieldErr if the field is not present.
|
|
*/
|
|
template <class T>
|
|
ValueProxy<T>
|
|
at(TypedField<T> const& f);
|
|
|
|
/** Return a modifiable field value as std::optional
|
|
|
|
@param An OptionaledField built from an SField value representing the
|
|
desired object field. In typical use, the OptionaledField will be
|
|
constructed by using the ~ operator on an SField.
|
|
@return Transparent proxy object to an `optional` holding a modifiable
|
|
reference to the value of the specified field. Returns
|
|
std::nullopt if the field is not present.
|
|
*/
|
|
template <class T>
|
|
OptionalProxy<T>
|
|
at(OptionaledField<T> const& of);
|
|
|
|
/** Set a field.
|
|
if the field already exists, it is replaced.
|
|
*/
|
|
void
|
|
set(std::unique_ptr<STBase> v);
|
|
|
|
void
|
|
set(STBase&& v);
|
|
|
|
void
|
|
setFieldU8(SField const& field, unsigned char);
|
|
void
|
|
setFieldU16(SField const& field, std::uint16_t);
|
|
void
|
|
setFieldU32(SField const& field, std::uint32_t);
|
|
void
|
|
setFieldU64(SField const& field, std::uint64_t);
|
|
void
|
|
setFieldH128(SField const& field, uint128 const&);
|
|
void
|
|
setFieldH256(SField const& field, uint256 const&);
|
|
void
|
|
setFieldI32(SField const& field, std::int32_t);
|
|
void
|
|
setFieldVL(SField const& field, Blob const&);
|
|
void
|
|
setFieldVL(SField const& field, Slice const&);
|
|
|
|
void
|
|
setAccountID(SField const& field, AccountID const&);
|
|
|
|
void
|
|
setFieldAmount(SField const& field, STAmount const&);
|
|
void
|
|
setFieldIssue(SField const& field, STIssue const&);
|
|
void
|
|
setFieldCurrency(SField const& field, STCurrency const&);
|
|
void
|
|
setFieldNumber(SField const& field, STNumber const&);
|
|
void
|
|
setFieldPathSet(SField const& field, STPathSet const&);
|
|
void
|
|
setFieldV256(SField const& field, STVector256 const& v);
|
|
void
|
|
setFieldArray(SField const& field, STArray const& v);
|
|
|
|
template <class Tag>
|
|
void
|
|
setFieldH160(SField const& field, base_uint<160, Tag> const& v);
|
|
|
|
STObject&
|
|
peekFieldObject(SField const& field);
|
|
STArray&
|
|
peekFieldArray(SField const& field);
|
|
|
|
bool
|
|
isFieldPresent(SField const& field) const;
|
|
STBase*
|
|
makeFieldPresent(SField const& field);
|
|
void
|
|
makeFieldAbsent(SField const& field);
|
|
bool
|
|
delField(SField const& field);
|
|
void
|
|
delField(int index);
|
|
|
|
bool
|
|
hasMatchingEntry(STBase const&);
|
|
|
|
bool
|
|
operator==(STObject const& o) const;
|
|
bool
|
|
operator!=(STObject const& o) const;
|
|
|
|
class FieldErr;
|
|
|
|
private:
|
|
enum WhichFields : bool {
|
|
// These values are carefully chosen to do the right thing if passed
|
|
// to SField::shouldInclude (bool)
|
|
omitSigningFields = false,
|
|
withAllFields = true
|
|
};
|
|
|
|
void
|
|
add(Serializer& s, WhichFields whichFields) const;
|
|
|
|
// Sort the entries in an STObject into the order that they will be
|
|
// serialized. Note: they are not sorted into pointer value order, they
|
|
// are sorted by SField::fieldCode.
|
|
static std::vector<STBase const*>
|
|
getSortedFields(STObject const& objToSort, WhichFields whichFields);
|
|
|
|
// Implementation for getting (most) fields that return by value.
|
|
//
|
|
// The remove_cv and remove_reference are necessitated by the STBitString
|
|
// types. Their value() returns by const ref. We return those types
|
|
// by value.
|
|
template <
|
|
typename T,
|
|
typename V = typename std::remove_cv<typename std::remove_reference<
|
|
decltype(std::declval<T>().value())>::type>::type>
|
|
V
|
|
getFieldByValue(SField const& field) const;
|
|
|
|
// Implementations for getting (most) fields that return by const reference.
|
|
//
|
|
// If an absent optional field is deserialized we don't have anything
|
|
// obvious to return. So we insist on having the call provide an
|
|
// 'empty' value we return in that circumstance.
|
|
template <typename T, typename V>
|
|
V const&
|
|
getFieldByConstRef(SField const& field, V const& empty) const;
|
|
|
|
// Implementation for setting most fields with a setValue() method.
|
|
template <typename T, typename V>
|
|
void
|
|
setFieldUsingSetValue(SField const& field, V value);
|
|
|
|
// Implementation for setting fields using assignment
|
|
template <typename T>
|
|
void
|
|
setFieldUsingAssignment(SField const& field, T const& value);
|
|
|
|
// Implementation for peeking STObjects and STArrays
|
|
template <typename T>
|
|
T&
|
|
peekField(SField const& field);
|
|
|
|
STBase*
|
|
copy(std::size_t n, void* buf) const override;
|
|
STBase*
|
|
move(std::size_t n, void* buf) override;
|
|
|
|
friend class detail::STVar;
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
template <class T>
|
|
class STObject::Proxy
|
|
{
|
|
public:
|
|
using value_type = typename T::value_type;
|
|
|
|
value_type
|
|
value() const;
|
|
|
|
value_type
|
|
operator*() const;
|
|
|
|
T const*
|
|
operator->() const;
|
|
|
|
protected:
|
|
STObject* st_;
|
|
SOEStyle style_;
|
|
TypedField<T> const* f_;
|
|
|
|
Proxy(Proxy const&) = default;
|
|
|
|
Proxy(STObject* st, TypedField<T> const* f);
|
|
|
|
T const*
|
|
find() const;
|
|
|
|
template <class U>
|
|
void
|
|
assign(U&& u);
|
|
};
|
|
|
|
// Constraint += and -= ValueProxy operators
|
|
// to value types that support arithmetic operations
|
|
template <typename U>
|
|
concept IsArithmetic = std::is_arithmetic_v<U> || std::is_same_v<U, STAmount>;
|
|
|
|
template <class T>
|
|
class STObject::ValueProxy : public Proxy<T>
|
|
{
|
|
private:
|
|
using value_type = typename T::value_type;
|
|
|
|
public:
|
|
ValueProxy(ValueProxy const&) = default;
|
|
ValueProxy&
|
|
operator=(ValueProxy const&) = delete;
|
|
|
|
template <class U>
|
|
std::enable_if_t<std::is_assignable_v<T, U>, ValueProxy&>
|
|
operator=(U&& u);
|
|
|
|
// Convenience operators for value types supporting
|
|
// arithmetic operations
|
|
template <IsArithmetic U>
|
|
ValueProxy&
|
|
operator+=(U const& u);
|
|
|
|
template <IsArithmetic U>
|
|
ValueProxy&
|
|
operator-=(U const& u);
|
|
|
|
operator value_type() const;
|
|
|
|
template <typename U>
|
|
friend bool
|
|
operator==(U const& lhs, STObject::ValueProxy<T> const& rhs)
|
|
{
|
|
return rhs.value() == lhs;
|
|
}
|
|
|
|
private:
|
|
friend class STObject;
|
|
|
|
ValueProxy(STObject* st, TypedField<T> const* f);
|
|
};
|
|
|
|
template <class T>
|
|
class STObject::OptionalProxy : public Proxy<T>
|
|
{
|
|
private:
|
|
using value_type = typename T::value_type;
|
|
|
|
using optional_type = std::optional<typename std::decay<value_type>::type>;
|
|
|
|
public:
|
|
OptionalProxy(OptionalProxy const&) = default;
|
|
OptionalProxy&
|
|
operator=(OptionalProxy const&) = delete;
|
|
|
|
/** Returns `true` if the field is set.
|
|
|
|
Fields with soeDEFAULT and set to the
|
|
default value will return `true`
|
|
*/
|
|
explicit
|
|
operator bool() const noexcept;
|
|
|
|
operator optional_type() const;
|
|
|
|
/** Explicit conversion to std::optional */
|
|
optional_type
|
|
operator~() const;
|
|
|
|
friend bool
|
|
operator==(OptionalProxy const& lhs, std::nullopt_t) noexcept
|
|
{
|
|
return !lhs.engaged();
|
|
}
|
|
|
|
friend bool
|
|
operator==(std::nullopt_t, OptionalProxy const& rhs) noexcept
|
|
{
|
|
return rhs == std::nullopt;
|
|
}
|
|
|
|
friend bool
|
|
operator==(OptionalProxy const& lhs, optional_type const& rhs) noexcept
|
|
{
|
|
if (!lhs.engaged())
|
|
return !rhs;
|
|
if (!rhs)
|
|
return false;
|
|
return *lhs == *rhs;
|
|
}
|
|
|
|
friend bool
|
|
operator==(optional_type const& lhs, OptionalProxy const& rhs) noexcept
|
|
{
|
|
return rhs == lhs;
|
|
}
|
|
|
|
friend bool
|
|
operator==(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
|
|
{
|
|
if (lhs.engaged() != rhs.engaged())
|
|
return false;
|
|
return !lhs.engaged() || *lhs == *rhs;
|
|
}
|
|
|
|
friend bool
|
|
operator!=(OptionalProxy const& lhs, std::nullopt_t) noexcept
|
|
{
|
|
return !(lhs == std::nullopt);
|
|
}
|
|
|
|
friend bool
|
|
operator!=(std::nullopt_t, OptionalProxy const& rhs) noexcept
|
|
{
|
|
return !(rhs == std::nullopt);
|
|
}
|
|
|
|
friend bool
|
|
operator!=(OptionalProxy const& lhs, optional_type const& rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
friend bool
|
|
operator!=(optional_type const& lhs, OptionalProxy const& rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
friend bool
|
|
operator!=(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
// Emulate std::optional::value_or
|
|
value_type
|
|
value_or(value_type val) const;
|
|
|
|
OptionalProxy&
|
|
operator=(std::nullopt_t const&);
|
|
OptionalProxy&
|
|
operator=(optional_type&& v);
|
|
OptionalProxy&
|
|
operator=(optional_type const& v);
|
|
|
|
template <class U>
|
|
std::enable_if_t<std::is_assignable_v<T, U>, OptionalProxy&>
|
|
operator=(U&& u);
|
|
|
|
private:
|
|
friend class STObject;
|
|
|
|
OptionalProxy(STObject* st, TypedField<T> const* f);
|
|
|
|
bool
|
|
engaged() const noexcept;
|
|
|
|
void
|
|
disengage();
|
|
|
|
optional_type
|
|
optional_value() const;
|
|
};
|
|
|
|
class STObject::FieldErr : public std::runtime_error
|
|
{
|
|
using std::runtime_error::runtime_error;
|
|
};
|
|
|
|
template <class T>
|
|
STObject::Proxy<T>::Proxy(STObject* st, TypedField<T> const* f) : st_(st), f_(f)
|
|
{
|
|
if (st_->mType)
|
|
{
|
|
// STObject has associated template
|
|
if (!st_->peekAtPField(*f_))
|
|
Throw<STObject::FieldErr>(
|
|
"Template field error '" + this->f_->getName() + "'");
|
|
style_ = st_->mType->style(*f_);
|
|
}
|
|
else
|
|
{
|
|
style_ = soeINVALID;
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
auto
|
|
STObject::Proxy<T>::value() const -> value_type
|
|
{
|
|
auto const t = find();
|
|
if (t)
|
|
return t->value();
|
|
if (style_ == soeINVALID)
|
|
{
|
|
Throw<STObject::FieldErr>("Value requested from invalid STObject.");
|
|
}
|
|
if (style_ != soeDEFAULT)
|
|
{
|
|
Throw<STObject::FieldErr>(
|
|
"Missing field '" + this->f_->getName() + "'");
|
|
}
|
|
return value_type{};
|
|
}
|
|
|
|
template <class T>
|
|
auto
|
|
STObject::Proxy<T>::operator*() const -> value_type
|
|
{
|
|
return this->value();
|
|
}
|
|
|
|
template <class T>
|
|
T const*
|
|
STObject::Proxy<T>::operator->() const
|
|
{
|
|
return this->find();
|
|
}
|
|
|
|
template <class T>
|
|
inline T const*
|
|
STObject::Proxy<T>::find() const
|
|
{
|
|
return dynamic_cast<T const*>(st_->peekAtPField(*f_));
|
|
}
|
|
|
|
template <class T>
|
|
template <class U>
|
|
void
|
|
STObject::Proxy<T>::assign(U&& u)
|
|
{
|
|
if (style_ == soeDEFAULT && u == value_type{})
|
|
{
|
|
st_->makeFieldAbsent(*f_);
|
|
return;
|
|
}
|
|
T* t;
|
|
if (style_ == soeINVALID)
|
|
t = dynamic_cast<T*>(st_->getPField(*f_, true));
|
|
else
|
|
t = dynamic_cast<T*>(st_->makeFieldPresent(*f_));
|
|
XRPL_ASSERT(t, "ripple::STObject::Proxy::assign : type cast succeeded");
|
|
*t = std::forward<U>(u);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
template <class T>
|
|
template <class U>
|
|
std::enable_if_t<std::is_assignable_v<T, U>, STObject::ValueProxy<T>&>
|
|
STObject::ValueProxy<T>::operator=(U&& u)
|
|
{
|
|
this->assign(std::forward<U>(u));
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
template <IsArithmetic U>
|
|
STObject::ValueProxy<T>&
|
|
STObject::ValueProxy<T>::operator+=(U const& u)
|
|
{
|
|
this->assign(this->value() + u);
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
template <IsArithmetic U>
|
|
STObject::ValueProxy<T>&
|
|
STObject::ValueProxy<T>::operator-=(U const& u)
|
|
{
|
|
this->assign(this->value() - u);
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
STObject::ValueProxy<T>::operator value_type() const
|
|
{
|
|
return this->value();
|
|
}
|
|
|
|
template <class T>
|
|
STObject::ValueProxy<T>::ValueProxy(STObject* st, TypedField<T> const* f)
|
|
: Proxy<T>(st, f)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
template <class T>
|
|
STObject::OptionalProxy<T>::operator bool() const noexcept
|
|
{
|
|
return engaged();
|
|
}
|
|
|
|
template <class T>
|
|
STObject::OptionalProxy<T>::operator typename STObject::OptionalProxy<
|
|
T>::optional_type() const
|
|
{
|
|
return optional_value();
|
|
}
|
|
|
|
template <class T>
|
|
typename STObject::OptionalProxy<T>::optional_type
|
|
STObject::OptionalProxy<T>::operator~() const
|
|
{
|
|
return optional_value();
|
|
}
|
|
|
|
template <class T>
|
|
auto
|
|
STObject::OptionalProxy<T>::operator=(std::nullopt_t const&) -> OptionalProxy&
|
|
{
|
|
disengage();
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
auto
|
|
STObject::OptionalProxy<T>::operator=(optional_type&& v) -> OptionalProxy&
|
|
{
|
|
if (v)
|
|
this->assign(std::move(*v));
|
|
else
|
|
disengage();
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
auto
|
|
STObject::OptionalProxy<T>::operator=(optional_type const& v) -> OptionalProxy&
|
|
{
|
|
if (v)
|
|
this->assign(*v);
|
|
else
|
|
disengage();
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
template <class U>
|
|
std::enable_if_t<std::is_assignable_v<T, U>, STObject::OptionalProxy<T>&>
|
|
STObject::OptionalProxy<T>::operator=(U&& u)
|
|
{
|
|
this->assign(std::forward<U>(u));
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
STObject::OptionalProxy<T>::OptionalProxy(STObject* st, TypedField<T> const* f)
|
|
: Proxy<T>(st, f)
|
|
{
|
|
}
|
|
|
|
template <class T>
|
|
bool
|
|
STObject::OptionalProxy<T>::engaged() const noexcept
|
|
{
|
|
return this->style_ == soeDEFAULT || this->find() != nullptr;
|
|
}
|
|
|
|
template <class T>
|
|
void
|
|
STObject::OptionalProxy<T>::disengage()
|
|
{
|
|
if (this->style_ == soeREQUIRED || this->style_ == soeDEFAULT)
|
|
Throw<STObject::FieldErr>(
|
|
"Template field error '" + this->f_->getName() + "'");
|
|
if (this->style_ == soeINVALID)
|
|
this->st_->delField(*this->f_);
|
|
else
|
|
this->st_->makeFieldAbsent(*this->f_);
|
|
}
|
|
|
|
template <class T>
|
|
auto
|
|
STObject::OptionalProxy<T>::optional_value() const -> optional_type
|
|
{
|
|
if (!engaged())
|
|
return std::nullopt;
|
|
return this->value();
|
|
}
|
|
|
|
template <class T>
|
|
typename STObject::OptionalProxy<T>::value_type
|
|
STObject::OptionalProxy<T>::value_or(value_type val) const
|
|
{
|
|
return engaged() ? this->value() : val;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
inline STBase const&
|
|
STObject::Transform::operator()(detail::STVar const& e) const
|
|
{
|
|
return e.get();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
inline STObject::STObject(SerialIter&& sit, SField const& name)
|
|
: STObject(sit, name)
|
|
{
|
|
}
|
|
|
|
inline STObject::iterator
|
|
STObject::begin() const
|
|
{
|
|
return iterator(v_.begin());
|
|
}
|
|
|
|
inline STObject::iterator
|
|
STObject::end() const
|
|
{
|
|
return iterator(v_.end());
|
|
}
|
|
|
|
inline bool
|
|
STObject::empty() const
|
|
{
|
|
return v_.empty();
|
|
}
|
|
|
|
inline void
|
|
STObject::reserve(std::size_t n)
|
|
{
|
|
v_.reserve(n);
|
|
}
|
|
|
|
inline bool
|
|
STObject::isFree() const
|
|
{
|
|
return mType == nullptr;
|
|
}
|
|
|
|
inline void
|
|
STObject::addWithoutSigningFields(Serializer& s) const
|
|
{
|
|
add(s, omitSigningFields);
|
|
}
|
|
|
|
// VFALCO NOTE does this return an expensive copy of an object with a
|
|
// dynamic buffer?
|
|
// VFALCO TODO Remove this function and fix the few callers.
|
|
inline Serializer
|
|
STObject::getSerializer() const
|
|
{
|
|
Serializer s;
|
|
add(s, withAllFields);
|
|
return s;
|
|
}
|
|
|
|
template <class... Args>
|
|
inline std::size_t
|
|
STObject::emplace_back(Args&&... args)
|
|
{
|
|
v_.emplace_back(std::forward<Args>(args)...);
|
|
return v_.size() - 1;
|
|
}
|
|
|
|
inline int
|
|
STObject::getCount() const
|
|
{
|
|
return v_.size();
|
|
}
|
|
|
|
inline STBase const&
|
|
STObject::peekAtIndex(int offset) const
|
|
{
|
|
return v_[offset].get();
|
|
}
|
|
|
|
inline STBase&
|
|
STObject::getIndex(int offset)
|
|
{
|
|
return v_[offset].get();
|
|
}
|
|
|
|
inline STBase const*
|
|
STObject::peekAtPIndex(int offset) const
|
|
{
|
|
return &v_[offset].get();
|
|
}
|
|
|
|
inline STBase*
|
|
STObject::getPIndex(int offset)
|
|
{
|
|
return &v_[offset].get();
|
|
}
|
|
|
|
template <class T>
|
|
typename T::value_type
|
|
STObject::operator[](TypedField<T> const& f) const
|
|
{
|
|
return at(f);
|
|
}
|
|
|
|
template <class T>
|
|
std::optional<std::decay_t<typename T::value_type>>
|
|
STObject::operator[](OptionaledField<T> const& of) const
|
|
{
|
|
return at(of);
|
|
}
|
|
|
|
template <class T>
|
|
inline auto
|
|
STObject::operator[](TypedField<T> const& f) -> ValueProxy<T>
|
|
{
|
|
return at(f);
|
|
}
|
|
|
|
template <class T>
|
|
inline auto
|
|
STObject::operator[](OptionaledField<T> const& of) -> OptionalProxy<T>
|
|
{
|
|
return at(of);
|
|
}
|
|
|
|
template <class T>
|
|
typename T::value_type
|
|
STObject::at(TypedField<T> const& f) const
|
|
{
|
|
auto const b = peekAtPField(f);
|
|
if (!b)
|
|
// This is a free object (no constraints)
|
|
// with no template
|
|
Throw<STObject::FieldErr>("Missing field: " + f.getName());
|
|
|
|
if (auto const u = dynamic_cast<T const*>(b))
|
|
return u->value();
|
|
|
|
XRPL_ASSERT(
|
|
mType,
|
|
"ripple::STObject::at(TypedField auto) : field template non-null");
|
|
XRPL_ASSERT(
|
|
b->getSType() == STI_NOTPRESENT,
|
|
"ripple::STObject::at(TypedField auto) : type not present");
|
|
|
|
if (mType->style(f) == soeOPTIONAL)
|
|
Throw<STObject::FieldErr>("Missing optional field: " + f.getName());
|
|
|
|
XRPL_ASSERT(
|
|
mType->style(f) == soeDEFAULT,
|
|
"ripple::STObject::at(TypedField auto) : template style is default");
|
|
|
|
// Used to help handle the case where value_type is a const reference,
|
|
// otherwise we would return the address of a temporary.
|
|
static std::decay_t<typename T::value_type> const dv{};
|
|
return dv;
|
|
}
|
|
|
|
template <class T>
|
|
std::optional<std::decay_t<typename T::value_type>>
|
|
STObject::at(OptionaledField<T> const& of) const
|
|
{
|
|
auto const b = peekAtPField(*of.f);
|
|
if (!b)
|
|
return std::nullopt;
|
|
auto const u = dynamic_cast<T const*>(b);
|
|
if (!u)
|
|
{
|
|
XRPL_ASSERT(
|
|
mType,
|
|
"ripple::STObject::at(OptionaledField auto) : field template "
|
|
"non-null");
|
|
XRPL_ASSERT(
|
|
b->getSType() == STI_NOTPRESENT,
|
|
"ripple::STObject::at(OptionaledField auto) : type not present");
|
|
if (mType->style(*of.f) == soeOPTIONAL)
|
|
return std::nullopt;
|
|
XRPL_ASSERT(
|
|
mType->style(*of.f) == soeDEFAULT,
|
|
"ripple::STObject::at(OptionaledField auto) : template style is "
|
|
"default");
|
|
return typename T::value_type{};
|
|
}
|
|
return u->value();
|
|
}
|
|
|
|
template <class T>
|
|
inline auto
|
|
STObject::at(TypedField<T> const& f) -> ValueProxy<T>
|
|
{
|
|
return ValueProxy<T>(this, &f);
|
|
}
|
|
|
|
template <class T>
|
|
inline auto
|
|
STObject::at(OptionaledField<T> const& of) -> OptionalProxy<T>
|
|
{
|
|
return OptionalProxy<T>(this, of.f);
|
|
}
|
|
|
|
template <class Tag>
|
|
void
|
|
STObject::setFieldH160(SField const& field, base_uint<160, Tag> const& v)
|
|
{
|
|
STBase* rf = getPField(field, true);
|
|
|
|
if (!rf)
|
|
throwFieldNotFound(field);
|
|
|
|
if (rf->getSType() == STI_NOTPRESENT)
|
|
rf = makeFieldPresent(field);
|
|
|
|
using Bits = STBitString<160>;
|
|
if (auto cf = dynamic_cast<Bits*>(rf))
|
|
cf->setValue(v);
|
|
else
|
|
Throw<std::runtime_error>("Wrong field type");
|
|
}
|
|
|
|
inline bool
|
|
STObject::operator!=(STObject const& o) const
|
|
{
|
|
return !(*this == o);
|
|
}
|
|
|
|
template <typename T, typename V>
|
|
V
|
|
STObject::getFieldByValue(SField const& field) const
|
|
{
|
|
STBase const* rf = peekAtPField(field);
|
|
|
|
if (!rf)
|
|
throwFieldNotFound(field);
|
|
|
|
SerializedTypeID id = rf->getSType();
|
|
|
|
if (id == STI_NOTPRESENT)
|
|
return V(); // optional field not present
|
|
|
|
T const* cf = dynamic_cast<T const*>(rf);
|
|
|
|
if (!cf)
|
|
Throw<std::runtime_error>("Wrong field type");
|
|
|
|
return cf->value();
|
|
}
|
|
|
|
// Implementations for getting (most) fields that return by const reference.
|
|
//
|
|
// If an absent optional field is deserialized we don't have anything
|
|
// obvious to return. So we insist on having the call provide an
|
|
// 'empty' value we return in that circumstance.
|
|
template <typename T, typename V>
|
|
V const&
|
|
STObject::getFieldByConstRef(SField const& field, V const& empty) const
|
|
{
|
|
STBase const* rf = peekAtPField(field);
|
|
|
|
if (!rf)
|
|
throwFieldNotFound(field);
|
|
|
|
SerializedTypeID id = rf->getSType();
|
|
|
|
if (id == STI_NOTPRESENT)
|
|
return empty; // optional field not present
|
|
|
|
T const* cf = dynamic_cast<T const*>(rf);
|
|
|
|
if (!cf)
|
|
Throw<std::runtime_error>("Wrong field type");
|
|
|
|
return *cf;
|
|
}
|
|
|
|
// Implementation for setting most fields with a setValue() method.
|
|
template <typename T, typename V>
|
|
void
|
|
STObject::setFieldUsingSetValue(SField const& field, V value)
|
|
{
|
|
static_assert(!std::is_lvalue_reference<V>::value, "");
|
|
|
|
STBase* rf = getPField(field, true);
|
|
|
|
if (!rf)
|
|
throwFieldNotFound(field);
|
|
|
|
if (rf->getSType() == STI_NOTPRESENT)
|
|
rf = makeFieldPresent(field);
|
|
|
|
T* cf = dynamic_cast<T*>(rf);
|
|
|
|
if (!cf)
|
|
Throw<std::runtime_error>("Wrong field type");
|
|
|
|
cf->setValue(std::move(value));
|
|
}
|
|
|
|
// Implementation for setting fields using assignment
|
|
template <typename T>
|
|
void
|
|
STObject::setFieldUsingAssignment(SField const& field, T const& value)
|
|
{
|
|
STBase* rf = getPField(field, true);
|
|
|
|
if (!rf)
|
|
throwFieldNotFound(field);
|
|
|
|
if (rf->getSType() == STI_NOTPRESENT)
|
|
rf = makeFieldPresent(field);
|
|
|
|
T* cf = dynamic_cast<T*>(rf);
|
|
|
|
if (!cf)
|
|
Throw<std::runtime_error>("Wrong field type");
|
|
|
|
(*cf) = value;
|
|
}
|
|
|
|
// Implementation for peeking STObjects and STArrays
|
|
template <typename T>
|
|
T&
|
|
STObject::peekField(SField const& field)
|
|
{
|
|
STBase* rf = getPField(field, true);
|
|
|
|
if (!rf)
|
|
throwFieldNotFound(field);
|
|
|
|
if (rf->getSType() == STI_NOTPRESENT)
|
|
rf = makeFieldPresent(field);
|
|
|
|
T* cf = dynamic_cast<T*>(rf);
|
|
|
|
if (!cf)
|
|
Throw<std::runtime_error>("Wrong field type");
|
|
|
|
return *cf;
|
|
}
|
|
|
|
} // namespace ripple
|
|
|
|
#endif
|