mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-20 02:25:53 +00:00
* fixInnerObjTemplate2 amendment: Apply inner object templates to all remaining (non-AMM) inner objects. Adds a unit test for applying the template to sfMajorities. Other remaining inner objects showed no problems having templates applied. * Move CMake directory * Rearrange sources * Rewrite includes * Recompute loops --------- Co-authored-by: Pretty Printer <cpp@ripple.com>
897 lines
20 KiB
C++
897 lines
20 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.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include <xrpl/basics/Log.h>
|
|
#include <xrpl/protocol/Feature.h>
|
|
#include <xrpl/protocol/InnerObjectFormats.h>
|
|
#include <xrpl/protocol/Rules.h>
|
|
#include <xrpl/protocol/STAccount.h>
|
|
#include <xrpl/protocol/STArray.h>
|
|
#include <xrpl/protocol/STBlob.h>
|
|
#include <xrpl/protocol/STCurrency.h>
|
|
#include <xrpl/protocol/STObject.h>
|
|
|
|
namespace ripple {
|
|
|
|
STObject::STObject(STObject&& other)
|
|
: STBase(other.getFName()), v_(std::move(other.v_)), mType(other.mType)
|
|
{
|
|
}
|
|
|
|
STObject::STObject(SField const& name) : STBase(name), mType(nullptr)
|
|
{
|
|
}
|
|
|
|
STObject::STObject(SOTemplate const& type, SField const& name) : STBase(name)
|
|
{
|
|
set(type);
|
|
}
|
|
|
|
STObject::STObject(SOTemplate const& type, SerialIter& sit, SField const& name)
|
|
: STBase(name)
|
|
{
|
|
v_.reserve(type.size());
|
|
set(sit);
|
|
applyTemplate(type); // May throw
|
|
}
|
|
|
|
STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(
|
|
false)
|
|
: STBase(name), mType(nullptr)
|
|
{
|
|
if (depth > 10)
|
|
Throw<std::runtime_error>("Maximum nesting depth of STObject exceeded");
|
|
set(sit, depth);
|
|
}
|
|
|
|
STObject
|
|
STObject::makeInnerObject(SField const& name)
|
|
{
|
|
STObject obj{name};
|
|
|
|
// The if is complicated because inner object templates were added in
|
|
// two phases:
|
|
// 1. If there are no available Rules, then always apply the template.
|
|
// 2. fixInnerObjTemplate added templates to two AMM inner objects.
|
|
// 3. fixInnerObjTemplate2 added templates to all remaining inner objects.
|
|
std::optional<Rules> const& rules = getCurrentTransactionRules();
|
|
bool const isAMMObj = name == sfAuctionSlot || name == sfVoteEntry;
|
|
if (!rules || (rules->enabled(fixInnerObjTemplate) && isAMMObj) ||
|
|
(rules->enabled(fixInnerObjTemplate2) && !isAMMObj))
|
|
{
|
|
if (SOTemplate const* elements =
|
|
InnerObjectFormats::getInstance().findSOTemplateBySField(name))
|
|
obj.set(*elements);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
STBase*
|
|
STObject::copy(std::size_t n, void* buf) const
|
|
{
|
|
return emplace(n, buf, *this);
|
|
}
|
|
|
|
STBase*
|
|
STObject::move(std::size_t n, void* buf)
|
|
{
|
|
return emplace(n, buf, std::move(*this));
|
|
}
|
|
|
|
SerializedTypeID
|
|
STObject::getSType() const
|
|
{
|
|
return STI_OBJECT;
|
|
}
|
|
|
|
bool
|
|
STObject::isDefault() const
|
|
{
|
|
return v_.empty();
|
|
}
|
|
|
|
void
|
|
STObject::add(Serializer& s) const
|
|
{
|
|
add(s, withAllFields); // just inner elements
|
|
}
|
|
|
|
STObject&
|
|
STObject::operator=(STObject&& other)
|
|
{
|
|
setFName(other.getFName());
|
|
mType = other.mType;
|
|
v_ = std::move(other.v_);
|
|
return *this;
|
|
}
|
|
|
|
void
|
|
STObject::set(const SOTemplate& type)
|
|
{
|
|
v_.clear();
|
|
v_.reserve(type.size());
|
|
mType = &type;
|
|
|
|
for (auto const& elem : type)
|
|
{
|
|
if (elem.style() != soeREQUIRED)
|
|
v_.emplace_back(detail::nonPresentObject, elem.sField());
|
|
else
|
|
v_.emplace_back(detail::defaultObject, elem.sField());
|
|
}
|
|
}
|
|
|
|
void
|
|
STObject::applyTemplate(const SOTemplate& type)
|
|
{
|
|
auto throwFieldErr = [](std::string const& field, char const* description) {
|
|
std::stringstream ss;
|
|
ss << "Field '" << field << "' " << description;
|
|
std::string text{ss.str()};
|
|
JLOG(debugLog().error()) << "STObject::applyTemplate failed: " << text;
|
|
Throw<FieldErr>(text);
|
|
};
|
|
|
|
mType = &type;
|
|
decltype(v_) v;
|
|
v.reserve(type.size());
|
|
for (auto const& e : type)
|
|
{
|
|
auto const iter =
|
|
std::find_if(v_.begin(), v_.end(), [&](detail::STVar const& b) {
|
|
return b.get().getFName() == e.sField();
|
|
});
|
|
if (iter != v_.end())
|
|
{
|
|
if ((e.style() == soeDEFAULT) && iter->get().isDefault())
|
|
{
|
|
throwFieldErr(
|
|
e.sField().fieldName,
|
|
"may not be explicitly set to default.");
|
|
}
|
|
v.emplace_back(std::move(*iter));
|
|
v_.erase(iter);
|
|
}
|
|
else
|
|
{
|
|
if (e.style() == soeREQUIRED)
|
|
{
|
|
throwFieldErr(e.sField().fieldName, "is required but missing.");
|
|
}
|
|
v.emplace_back(detail::nonPresentObject, e.sField());
|
|
}
|
|
}
|
|
for (auto const& e : v_)
|
|
{
|
|
// Anything left over in the object must be discardable
|
|
if (!e->getFName().isDiscardable())
|
|
{
|
|
throwFieldErr(
|
|
e->getFName().getName(), "found in disallowed location.");
|
|
}
|
|
}
|
|
// Swap the template matching data in for the old data,
|
|
// freeing any leftover junk
|
|
v_.swap(v);
|
|
}
|
|
|
|
void
|
|
STObject::applyTemplateFromSField(SField const& sField)
|
|
{
|
|
SOTemplate const* elements =
|
|
InnerObjectFormats::getInstance().findSOTemplateBySField(sField);
|
|
if (elements)
|
|
applyTemplate(*elements); // May throw
|
|
}
|
|
|
|
// return true = terminated with end-of-object
|
|
bool
|
|
STObject::set(SerialIter& sit, int depth)
|
|
{
|
|
bool reachedEndOfObject = false;
|
|
|
|
v_.clear();
|
|
|
|
uint8_t nop_counter = 0;
|
|
|
|
// Consume data in the pipe until we run out or reach the end
|
|
while (!sit.empty())
|
|
{
|
|
int type;
|
|
int field;
|
|
|
|
// Get the metadata for the next field
|
|
sit.getFieldID(type, field);
|
|
|
|
// pass nops
|
|
if (type == 9 && field == 9)
|
|
{
|
|
if (++nop_counter == 64)
|
|
{
|
|
JLOG(debugLog().error()) << "Too many NOPS";
|
|
Throw<std::runtime_error>("Too many NOPS");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// The object termination marker has been found and the termination
|
|
// marker has been consumed. Done deserializing.
|
|
if (type == STI_OBJECT && field == 1)
|
|
{
|
|
reachedEndOfObject = true;
|
|
break;
|
|
}
|
|
|
|
if (type == STI_ARRAY && field == 1)
|
|
{
|
|
JLOG(debugLog().error())
|
|
<< "Encountered object with embedded end-of-array marker";
|
|
Throw<std::runtime_error>("Illegal end-of-array marker in object");
|
|
}
|
|
|
|
auto const& fn = SField::getField(type, field);
|
|
|
|
if (fn.isInvalid())
|
|
{
|
|
JLOG(debugLog().error()) << "Unknown field: field_type=" << type
|
|
<< ", field_name=" << field;
|
|
std::stringstream ss;
|
|
ss << "Unknown field in Object t=" << type << " f=" << field;
|
|
|
|
Throw<std::runtime_error>(ss.str().c_str());
|
|
}
|
|
|
|
// Unflatten the field
|
|
v_.emplace_back(sit, fn, depth + 1);
|
|
|
|
// If the object type has a known SOTemplate then set it.
|
|
if (auto const obj = dynamic_cast<STObject*>(&(v_.back().get())))
|
|
obj->applyTemplateFromSField(fn); // May throw
|
|
}
|
|
|
|
// We want to ensure that the deserialized object does not contain any
|
|
// duplicate fields. This is a key invariant:
|
|
auto const sf = getSortedFields(*this, withAllFields);
|
|
|
|
auto const dup = std::adjacent_find(
|
|
sf.cbegin(), sf.cend(), [](STBase const* lhs, STBase const* rhs) {
|
|
return lhs->getFName() == rhs->getFName();
|
|
});
|
|
|
|
if (dup != sf.cend())
|
|
Throw<std::runtime_error>("Duplicate field detected");
|
|
|
|
return reachedEndOfObject;
|
|
}
|
|
|
|
bool
|
|
STObject::hasMatchingEntry(const STBase& t)
|
|
{
|
|
const STBase* o = peekAtPField(t.getFName());
|
|
|
|
if (!o)
|
|
return false;
|
|
|
|
return t == *o;
|
|
}
|
|
|
|
std::string
|
|
STObject::getFullText() const
|
|
{
|
|
std::string ret;
|
|
bool first = true;
|
|
|
|
if (getFName().hasName())
|
|
{
|
|
ret = getFName().getName();
|
|
ret += " = {";
|
|
}
|
|
else
|
|
ret = "{";
|
|
|
|
for (auto const& elem : v_)
|
|
{
|
|
if (elem->getSType() != STI_NOTPRESENT)
|
|
{
|
|
if (!first)
|
|
ret += ", ";
|
|
else
|
|
first = false;
|
|
|
|
ret += elem->getFullText();
|
|
}
|
|
}
|
|
|
|
ret += "}";
|
|
return ret;
|
|
}
|
|
|
|
std::string
|
|
STObject::getText() const
|
|
{
|
|
std::string ret = "{";
|
|
bool first = false;
|
|
for (auto const& elem : v_)
|
|
{
|
|
if (!first)
|
|
{
|
|
ret += ", ";
|
|
first = false;
|
|
}
|
|
|
|
ret += elem->getText();
|
|
}
|
|
ret += "}";
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
STObject::isEquivalent(const STBase& t) const
|
|
{
|
|
const STObject* v = dynamic_cast<const STObject*>(&t);
|
|
|
|
if (!v)
|
|
return false;
|
|
|
|
if (mType != nullptr && v->mType == mType)
|
|
{
|
|
return std::equal(
|
|
begin(),
|
|
end(),
|
|
v->begin(),
|
|
v->end(),
|
|
[](STBase const& st1, STBase const& st2) {
|
|
return (st1.getSType() == st2.getSType()) &&
|
|
st1.isEquivalent(st2);
|
|
});
|
|
}
|
|
|
|
auto const sf1 = getSortedFields(*this, withAllFields);
|
|
auto const sf2 = getSortedFields(*v, withAllFields);
|
|
|
|
return std::equal(
|
|
sf1.begin(),
|
|
sf1.end(),
|
|
sf2.begin(),
|
|
sf2.end(),
|
|
[](STBase const* st1, STBase const* st2) {
|
|
return (st1->getSType() == st2->getSType()) &&
|
|
st1->isEquivalent(*st2);
|
|
});
|
|
}
|
|
|
|
uint256
|
|
STObject::getHash(HashPrefix prefix) const
|
|
{
|
|
Serializer s;
|
|
s.add32(prefix);
|
|
add(s, withAllFields);
|
|
return s.getSHA512Half();
|
|
}
|
|
|
|
uint256
|
|
STObject::getSigningHash(HashPrefix prefix) const
|
|
{
|
|
Serializer s;
|
|
s.add32(prefix);
|
|
add(s, omitSigningFields);
|
|
return s.getSHA512Half();
|
|
}
|
|
|
|
int
|
|
STObject::getFieldIndex(SField const& field) const
|
|
{
|
|
if (mType != nullptr)
|
|
return mType->getIndex(field);
|
|
|
|
int i = 0;
|
|
for (auto const& elem : v_)
|
|
{
|
|
if (elem->getFName() == field)
|
|
return i;
|
|
++i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const STBase&
|
|
STObject::peekAtField(SField const& field) const
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
throwFieldNotFound(field);
|
|
|
|
return peekAtIndex(index);
|
|
}
|
|
|
|
STBase&
|
|
STObject::getField(SField const& field)
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
throwFieldNotFound(field);
|
|
|
|
return getIndex(index);
|
|
}
|
|
|
|
SField const&
|
|
STObject::getFieldSType(int index) const
|
|
{
|
|
return v_[index]->getFName();
|
|
}
|
|
|
|
const STBase*
|
|
STObject::peekAtPField(SField const& field) const
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
return nullptr;
|
|
|
|
return peekAtPIndex(index);
|
|
}
|
|
|
|
STBase*
|
|
STObject::getPField(SField const& field, bool createOkay)
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
{
|
|
if (createOkay && isFree())
|
|
return getPIndex(emplace_back(detail::defaultObject, field));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
return getPIndex(index);
|
|
}
|
|
|
|
bool
|
|
STObject::isFieldPresent(SField const& field) const
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
return false;
|
|
|
|
return peekAtIndex(index).getSType() != STI_NOTPRESENT;
|
|
}
|
|
|
|
STObject&
|
|
STObject::peekFieldObject(SField const& field)
|
|
{
|
|
return peekField<STObject>(field);
|
|
}
|
|
|
|
STArray&
|
|
STObject::peekFieldArray(SField const& field)
|
|
{
|
|
return peekField<STArray>(field);
|
|
}
|
|
|
|
bool
|
|
STObject::setFlag(std::uint32_t f)
|
|
{
|
|
STUInt32* t = dynamic_cast<STUInt32*>(getPField(sfFlags, true));
|
|
|
|
if (!t)
|
|
return false;
|
|
|
|
t->setValue(t->value() | f);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
STObject::clearFlag(std::uint32_t f)
|
|
{
|
|
STUInt32* t = dynamic_cast<STUInt32*>(getPField(sfFlags));
|
|
|
|
if (!t)
|
|
return false;
|
|
|
|
t->setValue(t->value() & ~f);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
STObject::isFlag(std::uint32_t f) const
|
|
{
|
|
return (getFlags() & f) == f;
|
|
}
|
|
|
|
std::uint32_t
|
|
STObject::getFlags(void) const
|
|
{
|
|
const STUInt32* t = dynamic_cast<const STUInt32*>(peekAtPField(sfFlags));
|
|
|
|
if (!t)
|
|
return 0;
|
|
|
|
return t->value();
|
|
}
|
|
|
|
STBase*
|
|
STObject::makeFieldPresent(SField const& field)
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
{
|
|
if (!isFree())
|
|
throwFieldNotFound(field);
|
|
|
|
return getPIndex(emplace_back(detail::nonPresentObject, field));
|
|
}
|
|
|
|
STBase* f = getPIndex(index);
|
|
|
|
if (f->getSType() != STI_NOTPRESENT)
|
|
return f;
|
|
|
|
v_[index] = detail::STVar(detail::defaultObject, f->getFName());
|
|
return getPIndex(index);
|
|
}
|
|
|
|
void
|
|
STObject::makeFieldAbsent(SField const& field)
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
throwFieldNotFound(field);
|
|
|
|
const STBase& f = peekAtIndex(index);
|
|
|
|
if (f.getSType() == STI_NOTPRESENT)
|
|
return;
|
|
v_[index] = detail::STVar(detail::nonPresentObject, f.getFName());
|
|
}
|
|
|
|
bool
|
|
STObject::delField(SField const& field)
|
|
{
|
|
int index = getFieldIndex(field);
|
|
|
|
if (index == -1)
|
|
return false;
|
|
|
|
delField(index);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
STObject::delField(int index)
|
|
{
|
|
v_.erase(v_.begin() + index);
|
|
}
|
|
|
|
unsigned char
|
|
STObject::getFieldU8(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt8>(field);
|
|
}
|
|
|
|
std::uint16_t
|
|
STObject::getFieldU16(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt16>(field);
|
|
}
|
|
|
|
std::uint32_t
|
|
STObject::getFieldU32(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt32>(field);
|
|
}
|
|
|
|
std::uint64_t
|
|
STObject::getFieldU64(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt64>(field);
|
|
}
|
|
|
|
uint128
|
|
STObject::getFieldH128(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt128>(field);
|
|
}
|
|
|
|
uint160
|
|
STObject::getFieldH160(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt160>(field);
|
|
}
|
|
|
|
uint256
|
|
STObject::getFieldH256(SField const& field) const
|
|
{
|
|
return getFieldByValue<STUInt256>(field);
|
|
}
|
|
|
|
AccountID
|
|
STObject::getAccountID(SField const& field) const
|
|
{
|
|
return getFieldByValue<STAccount>(field);
|
|
}
|
|
|
|
Blob
|
|
STObject::getFieldVL(SField const& field) const
|
|
{
|
|
STBlob empty;
|
|
STBlob const& b = getFieldByConstRef<STBlob>(field, empty);
|
|
return Blob(b.data(), b.data() + b.size());
|
|
}
|
|
|
|
STAmount const&
|
|
STObject::getFieldAmount(SField const& field) const
|
|
{
|
|
static STAmount const empty{};
|
|
return getFieldByConstRef<STAmount>(field, empty);
|
|
}
|
|
|
|
STPathSet const&
|
|
STObject::getFieldPathSet(SField const& field) const
|
|
{
|
|
static STPathSet const empty{};
|
|
return getFieldByConstRef<STPathSet>(field, empty);
|
|
}
|
|
|
|
const STVector256&
|
|
STObject::getFieldV256(SField const& field) const
|
|
{
|
|
static STVector256 const empty{};
|
|
return getFieldByConstRef<STVector256>(field, empty);
|
|
}
|
|
|
|
const STArray&
|
|
STObject::getFieldArray(SField const& field) const
|
|
{
|
|
static STArray const empty{};
|
|
return getFieldByConstRef<STArray>(field, empty);
|
|
}
|
|
|
|
STCurrency const&
|
|
STObject::getFieldCurrency(SField const& field) const
|
|
{
|
|
static STCurrency const empty{};
|
|
return getFieldByConstRef<STCurrency>(field, empty);
|
|
}
|
|
|
|
void
|
|
STObject::set(std::unique_ptr<STBase> v)
|
|
{
|
|
set(std::move(*v.get()));
|
|
}
|
|
|
|
void
|
|
STObject::set(STBase&& v)
|
|
{
|
|
auto const i = getFieldIndex(v.getFName());
|
|
if (i != -1)
|
|
{
|
|
v_[i] = std::move(v);
|
|
}
|
|
else
|
|
{
|
|
if (!isFree())
|
|
Throw<std::runtime_error>("missing field in templated STObject");
|
|
v_.emplace_back(std::move(v));
|
|
}
|
|
}
|
|
|
|
void
|
|
STObject::setFieldU8(SField const& field, unsigned char v)
|
|
{
|
|
setFieldUsingSetValue<STUInt8>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldU16(SField const& field, std::uint16_t v)
|
|
{
|
|
setFieldUsingSetValue<STUInt16>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldU32(SField const& field, std::uint32_t v)
|
|
{
|
|
setFieldUsingSetValue<STUInt32>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldU64(SField const& field, std::uint64_t v)
|
|
{
|
|
setFieldUsingSetValue<STUInt64>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldH128(SField const& field, uint128 const& v)
|
|
{
|
|
setFieldUsingSetValue<STUInt128>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldH256(SField const& field, uint256 const& v)
|
|
{
|
|
setFieldUsingSetValue<STUInt256>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldV256(SField const& field, STVector256 const& v)
|
|
{
|
|
setFieldUsingSetValue<STVector256>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setAccountID(SField const& field, AccountID const& v)
|
|
{
|
|
setFieldUsingSetValue<STAccount>(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldVL(SField const& field, Blob const& v)
|
|
{
|
|
setFieldUsingSetValue<STBlob>(field, Buffer(v.data(), v.size()));
|
|
}
|
|
|
|
void
|
|
STObject::setFieldVL(SField const& field, Slice const& s)
|
|
{
|
|
setFieldUsingSetValue<STBlob>(field, Buffer(s.data(), s.size()));
|
|
}
|
|
|
|
void
|
|
STObject::setFieldAmount(SField const& field, STAmount const& v)
|
|
{
|
|
setFieldUsingAssignment(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldCurrency(SField const& field, STCurrency const& v)
|
|
{
|
|
setFieldUsingAssignment(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldIssue(SField const& field, STIssue const& v)
|
|
{
|
|
setFieldUsingAssignment(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldPathSet(SField const& field, STPathSet const& v)
|
|
{
|
|
setFieldUsingAssignment(field, v);
|
|
}
|
|
|
|
void
|
|
STObject::setFieldArray(SField const& field, STArray const& v)
|
|
{
|
|
setFieldUsingAssignment(field, v);
|
|
}
|
|
|
|
Json::Value
|
|
STObject::getJson(JsonOptions options) const
|
|
{
|
|
Json::Value ret(Json::objectValue);
|
|
|
|
for (auto const& elem : v_)
|
|
{
|
|
if (elem->getSType() != STI_NOTPRESENT)
|
|
ret[elem->getFName().getJsonName()] = elem->getJson(options);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
STObject::operator==(const STObject& obj) const
|
|
{
|
|
// This is not particularly efficient, and only compares data elements
|
|
// with binary representations
|
|
int matches = 0;
|
|
for (auto const& t1 : v_)
|
|
{
|
|
if ((t1->getSType() != STI_NOTPRESENT) && t1->getFName().isBinary())
|
|
{
|
|
// each present field must have a matching field
|
|
bool match = false;
|
|
for (auto const& t2 : obj.v_)
|
|
{
|
|
if (t1->getFName() == t2->getFName())
|
|
{
|
|
if (t2 != t1)
|
|
return false;
|
|
|
|
match = true;
|
|
++matches;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int fields = 0;
|
|
for (auto const& t2 : obj.v_)
|
|
{
|
|
if ((t2->getSType() != STI_NOTPRESENT) && t2->getFName().isBinary())
|
|
++fields;
|
|
}
|
|
|
|
if (fields != matches)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
STObject::add(Serializer& s, WhichFields whichFields) const
|
|
{
|
|
// Depending on whichFields, signing fields are either serialized or
|
|
// not. Then fields are added to the Serializer sorted by fieldCode.
|
|
std::vector<STBase const*> const fields{
|
|
getSortedFields(*this, whichFields)};
|
|
|
|
// insert sorted
|
|
for (STBase const* const field : fields)
|
|
{
|
|
// When we serialize an object inside another object,
|
|
// the type associated by rule with this field name
|
|
// must be OBJECT, or the object cannot be deserialized
|
|
SerializedTypeID const sType{field->getSType()};
|
|
assert(
|
|
(sType != STI_OBJECT) ||
|
|
(field->getFName().fieldType == STI_OBJECT));
|
|
field->addFieldID(s);
|
|
field->add(s);
|
|
if (sType == STI_ARRAY || sType == STI_OBJECT)
|
|
s.addFieldID(sType, 1);
|
|
}
|
|
}
|
|
|
|
std::vector<STBase const*>
|
|
STObject::getSortedFields(STObject const& objToSort, WhichFields whichFields)
|
|
{
|
|
std::vector<STBase const*> sf;
|
|
sf.reserve(objToSort.getCount());
|
|
|
|
// Choose the fields that we need to sort.
|
|
for (detail::STVar const& elem : objToSort.v_)
|
|
{
|
|
STBase const& base = elem.get();
|
|
if ((base.getSType() != STI_NOTPRESENT) &&
|
|
base.getFName().shouldInclude(whichFields))
|
|
{
|
|
sf.push_back(&base);
|
|
}
|
|
}
|
|
|
|
// Sort the fields by fieldCode.
|
|
std::sort(sf.begin(), sf.end(), [](STBase const* lhs, STBase const* rhs) {
|
|
return lhs->getFName().fieldCode < rhs->getFName().fieldCode;
|
|
});
|
|
|
|
return sf;
|
|
}
|
|
|
|
} // namespace ripple
|